{"article":{"slug":"claude-code-burns-15-20-of-your-tokens-on-a-3-word-status-line-heres-how-to-turn-it-off","title":"Claude Code burns ~15–20% of your tokens on a 3-word status line. Here's how to turn it off","subtitle":null,"summary":"Flamerix AI reports that Claude Code's agent_summary feature sends each background agent's full transcript to its model about every 30 seconds just to produce a 3-5 word status line, measuring it at about 16% of Claude Code spend, and publishes an MIT-licensed Bun preload that turns it off without modifying the binary, with install, verification, tested versions and a timeline.","content_type":"tutorial","language":"en","canonical_url":"https://gist.github.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce","author":{"name":"Flamerix AI","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Flamerix AI","url":"https://flamerix.ai","listing_slug":null,"listing":null},"topics":[{"name":"AI","slug":"ai","url":"https://listedarticles.com/topics/ai"},{"name":"Developer Tools","slug":"developer-tools","url":"https://listedarticles.com/topics/developer-tools"},{"name":"LLMs","slug":"llms","url":"https://listedarticles.com/topics/llms"}],"about_listings":[],"cover_image_url":null,"license":"MIT","word_count":1722,"reading_minutes":7,"published_at":"2026-10-29T05:12:31.390Z","added_at":"2026-10-29T05:12:31.390Z","updated_at":"2026-10-29T05:12:31.390Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/claude-code-burns-15-20-of-your-tokens-on-a-3-word-status-line-heres-how-to-turn-it-off","markdown_url":"https://listedarticles.com/articles/claude-code-burns-15-20-of-your-tokens-on-a-3-word-status-line-heres-how-to-turn-it-off.md","example":false,"citation":"Flamerix AI, Flamerix AI. \"Claude Code burns ~15–20% of your tokens on a 3-word status line. Here's how to turn it off.\" 29 Oct 2026. https://gist.github.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce (MIT)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://gist.github.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce"},"body_markdown":"by [Flamerix AI](https://flamerix.ai) · MIT License\n\n> Tested on Linux, Claude Code 2.1.257–2.1.312 (see Tested versions). The binary is not modified (`BUN_OPTIONS=--preload`). An update can silently undo it, so see Verify.\n\n## TL;DR\n\n```\ncurl -fsSL https://gist.githubusercontent.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce/raw/no-agent-summary.js -o ~/.local/share/no-agent-summary.js\nclaude daemon stop --any\nBUN_OPTIONS=\"${BUN_OPTIONS:+$BUN_OPTIONS }--preload $HOME/.local/share/no-agent-summary.js\" claude\n```\n\nTo make it permanent for every Claude Code process, see [Install](#install).\n\n## The problem\n\nEvery ~30 seconds, for every background agent, Claude Code sends that agent's **entire\ntranscript** to the agent's own model (usually Opus) and asks it to \"describe your most recent\naction in 3-5 words\". The answer only updates the agent's status line in the agents panel; it\nnever reaches the model or your conversation. There is no setting that turns it off, and since\n2.1.232 (August 2026) it has been on by default.\n\nThe anomaly was detected by [DRIM](https://flamerix.ai) in Claude Code's OpenTelemetry export:\ngrouping `claude_code.api_request` events by `query_source` showed `agent_summary` costing **more than the main conversation itself**.\n\n| `query_source = agent_summary` | week before the hook | 2 days after |\n| --- | --- | --- |\n| share of all Claude Code spend | **16.4 %** (main conversation: 15.7 %) | **0 %** |\n| share of all API requests | **19.7 %**, about 1 in 5 | **0 %** |\n| part of that spend on Opus | 96 % | — |\n| input tokens per output token | ≈ 9,000 : 1 | — |\n\nSpend here means Claude Code's own per-request estimate (`cost_usd`), not an invoice: on a\nsubscription, what you lose is rate-limit headroom. We are not the only ones who measured it:\n[#86879](https://github.com/anthropics/claude-code/issues/86879) collects four independent\nmeasurements of 18–26 %.\n\n## The fix (how it works)\n\n`no-agent-summary.js` is a Bun preload (~80 lines of code). It wraps `setTimeout`/`setInterval`;\nwhen a callback's source text contains a summarizer anchor (e.g. the `[AgentSummary] Timer fired`\nlog line), it returns an inert handle instead of arming the timer. Every other timer passes\nthrough untouched. In the OpenTelemetry export the suppressed requests are\n`claude_code.api_request` events with `query_source = agent_summary`.\n\nWhy a timer hook rather than something simpler: the module ships as precompiled Bun bytecode, so\nediting its JS text inside the binary changes nothing, and the API client does not go through a\nwrapped `globalThis.fetch`. But `Function.prototype.toString` still returns a callback's source\neven when it runs from bytecode, so the timer can be recognised by a string inside it. The inert\ntimer uses exactly 2³¹−1 ms: Node and Bun treat a larger delay as 1 ms, so an \"infinite\" delay\nwould fire at once.\n\n## Install\n\n1. Put `no-agent-summary.js` somewhere stable; note its **absolute** path.\n2. Set `BUN_OPTIONS` **globally** so every Claude Code process gets it. One of:\n\n   ```\n   # ~/.profile, ~/.bashrc, ~/.zshrc\n   export BUN_OPTIONS=\"${BUN_OPTIONS:+$BUN_OPTIONS }--preload /abs/path/no-agent-summary.js\"\n   ```\n\n   ```\n   # ~/.config/fish/config.fish\n   set -gx BUN_OPTIONS \"--preload /abs/path/no-agent-summary.js\"\n   ```\n\n   ```\n   # ~/.config/environment.d/50-no-agent-summary.conf (systemd user env; also reaches\n   # user services and timers; applies when the user manager next starts, usually next login)\n   BUN_OPTIONS=--preload /abs/path/no-agent-summary.js\n   ```\n\n   Or a wrapper named `claude` earlier on `PATH` (the `case` keeps it idempotent when one Claude\n   Code process starts another):\n\n   ```\n   #!/bin/sh\n   HOOK=/abs/path/no-agent-summary.js\n   case \" ${BUN_OPTIONS-} \" in\n     *\" --preload $HOOK \"*) ;;\n     *) export BUN_OPTIONS=\"${BUN_OPTIONS:+$BUN_OPTIONS }--preload $HOOK\" ;;\n   esac\n   : \"${CC_AGENT_SUMMARY_LOG:=${XDG_RUNTIME_DIR:-/tmp}/no-agent-summary.log}\"\n   export CC_AGENT_SUMMARY_LOG\n   exec /path/to/the/real/claude \"$@\"\n   ```\n3. Restart the daemon and every already-running session, then start Claude Code from an\n   environment that has `BUN_OPTIONS`:\n\n   ```\n   claude daemon stop --any\n   ```\n\n### Env\n\n| variable | values | effect |\n| --- | --- | --- |\n| `CC_AGENT_SUMMARY_MODE` | `off` (default), `pass` | `off` suppresses the summary timer. `pass` is the A/B control: loads and logs, arms every timer untouched. Any other value warns on stderr and acts as `off`. |\n| `CC_AGENT_SUMMARY_LOG` | file path, optional | Appends `preload active mode=… bun=… hooked=…` on load, one line per matched timer (first 20), and an exit count. |\n| `CC_AGENT_SUMMARY_MARKER` | file path, optional | Overwritten on load with the `preload active …` line. |\n\n## Nuances\n\n- **Restart the daemon after install.** The daemon, pty host and pre-warmed `bg-spare` sessions\n  inherit the environment of the client that started the daemon, so a daemon started before\n  `BUN_OPTIONS` was set keeps spawning unhooked sessions. Hence `claude daemon stop --any`.\n- **Headless runs need it too:** `claude -p`, cron, CI, editor integrations.\n- **Platform:** Linux tested, macOS and Windows untested. On Windows the `/$bunfs/` gate most\n  likely never installs the hook.\n- **A/B:** `CC_AGENT_SUMMARY_MODE=pass` loads the hook but arms every timer; the summaries come back, which shows the `off` result is the hook's doing.\n- **Background processes:** daemon, pty host and `bg-spare` must carry `--preload` in their environment (`/proc/<pid>/environ` on Linux); if not, restart the daemon.\n\n## Verify (after every update)\n\n1. **Preload loads:**\n\n   ```\n   BUN_OPTIONS=\"--preload $HOME/.local/share/no-agent-summary.js\" CC_AGENT_SUMMARY_LOG=/tmp/nas.log claude --version && cat /tmp/nas.log\n   ```\n\n   Expect `preload active mode=off …`. No line: `BUN_OPTIONS` did not reach the process, or the\n   path is not absolute.\n2. **Timer suppressed:** with the same `BUN_OPTIONS` and `CC_AGENT_SUMMARY_LOG=/tmp/nas.log`, run\n   a background subagent for a minute or two. The log shows\n   `summary timer #1 suppressed (setTimeout, delay=30000)` and the agent's panel status stops\n   updating. If you export OpenTelemetry, `query_source = agent_summary` should stay at zero; this\n   is the only check that catches every failure mode.\n\n### Fail-open\n\nMatching is by **source text**: an anchor must sit inside the callback passed to the timer. If an\nupdate breaks that, the hook matches nothing and summaries silently come back. Ways it can happen:\n\n- the log line or query-source id is renamed;\n- the callback is passed via `fn.bind(...)` (no source in `toString`) or another wrapper;\n- the strings move to a shared constant outside the callback;\n- the timer moves to an unwrapped API (`timers/promises`, a scheduler loop).\n\nSo re-run Verify after every update.\n\n## Effect\n\n- We run it under every Claude Code process on our machine (2.1.301). Since then there have been\n  zero `agent_summary` requests, and no other side query grew to take its place.\n- Subagents, background agents, forked skills and background sessions run normally; only the\n  summary fork is skipped.\n- Visible change: the per-agent panel status stops updating; SDK `task_progress` has no `summary`.\n- Overhead: one `Function.prototype.toString` per distinct timer callback, cached in a `WeakMap`.\n  `claude --version`: ~10 ms → ~17 ms (one measurement).\n\n## Tested versions\n\nLinux x64 binary from npm `@anthropic-ai/claude-code-linux-x64` (its sha256 equals the native\ninstaller's `manifest.json` checksum). **Static:** the extracted bundle's summary timer callback\ncarries a hook anchor. **Live:** `claude -p --model haiku` with\n`CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES=1`, one subagent doing 8 steps of ~20 s, counting\n`[AgentSummary] Forking` in `--debug-file`.\n\n| version | published | sha256 | static | summary forks in a ~2.5-min agent run: no hook / hook `pass` / hook `off` | notes |\n| --- | --- | --- | --- | --- | --- |\n| 2.1.312 | 2026-10-27 | `4f4178ce41d8` | OK | **5 / 5 / 0** | `latest` on 2026-10-28; interactive: 6 no hook, 0 `off` |\n| 2.1.310 | 2026-10-23 | `b73676430af1` | OK | **5 / 5 / 0** |  |\n| 2.1.308 | 2026-10-22 | `356733f185fa` | OK | **5 / 6 / 0** | `stable` on 2026-10-28; the `pass` run's agent took 9 steps instead of 8 |\n| 2.1.301 | 2026-10-15 | `bc1aad9982c7` | OK | **5 / 5 / 0** | same with earlier hook builds; interactive `off` also 0; in daily use since 2026-10-26, 0 `agent_summary` requests in telemetry |\n| 2.1.280 | 2026-09-22 | `1e08503dbdf3` | OK | **1 / 1 / 0** (timer fired 11 / 11 / 0) | interactive: `-p` cannot enable summaries on this version. The agent sat at a permission prompt, so later timers skipped the unchanged transcript |\n| 2.1.257 | 2026-09-01 | `9a64bda9d872` | OK | **1 / 1 / 0** (timer fired 11 / 11 / 0) | same as 2.1.280 |\n\n5 / 5 / 0 = the fork happens on this version, merely loading the hook changes nothing, and `off`\nremoves it.\n\n## Why there is no setting for this\n\n- **The documented switch only turns it on.** The SDK option `agentProgressSummaries` defaults to\n  `false`, but in the bundle it is one term of an OR, and every code path that writes it sets\n  `true`. So `false` changes nothing, and the undocumented `CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES`\n  can only enable summaries too.\n- **The interval belongs to the server.** It comes from a per-model-family server flag and is\n  clamped to 30–240 s. The default is already the minimum, and no value disables the timer.\n- **The obvious env vars miss.** `DISABLE_NON_ESSENTIAL_MODEL_CALLS`, which some packagers set,\n  does not occur in the binary at all. `CLAUDE_CODE_FORK_SUBAGENT=0` and\n  `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` do stop it, but only by taking fork subagents or\n  background agents with it.\n- **The prompt cache makes it cheaper, not cheap.** About 98 % of these calls re-read the\n  transcript from cache; the ~2 % that hit a cold cache carry over a third of its cost.\n- **Cost tools that read transcripts don't see it.** These requests are not written to the\n  session logs on disk ([#104358](https://github.com/anthropics/claude-code/issues/104358)); only\n  the OpenTelemetry export shows them. The docs section on background token usage doesn't list it\n  either, while Agent View's row summaries are documented as a Haiku-class model refreshing every\n  few minutes.\n- **A cheaper variant already exists, switched off.** 2.1.310 ships a `short` mode: about 2k\n  tokens of notes instead of the full transcript, optionally written by a smaller model. The\n  default is still the full transcript, and only server flags can change it.\n\n### Timeline (2026)\n\n| version (published) | what changed |\n| --- | --- |\n| 2.1.32 (Feb 5) | the code first appears; dead in public builds |\n| 2.1.72 (Mar 9) | reachable as an SDK opt-in |\n| 2.1.117 (Apr 21) | reachable in the interactive CLI behind the fork gate or a server rollout |\n| ≤ 2.1.220 (Jul 24) | forked skills are always summarized |\n| **2.1.232 (Aug 13)** | **on by default** for interactive background agents |\n| 2.1.300 (Oct 14) | interval becomes server-tunable, 30–240 s |\n| 2.1.310 (Oct 23) | the cheaper `short` mode lands, switched off |\n\nIn our telemetry its weekly share of spend went from 3.5–7 % in July (the fork gate was already\nopen for us) to 7.3 % and then 11.8 % in the week we picked up 2.1.233, and to about 16 % by\nmid-October.\n\n## License and disclaimer\n\nMIT © 2026 [Flamerix AI](https://flamerix.ai). Provided **as is**, without warranty of any kind;\nyou use it at your own risk. Flamerix AI is not liable for any damage, data loss or account\naction arising from its use. Changing a client's runtime behaviour may conflict with the terms of\nthe service you use it with; checking that is on you.\n\n\"Claude\" and \"Claude Code\" are trademarks of Anthropic, PBC. This project is independent and is\nnot affiliated with, sponsored or endorsed by Anthropic.\n","body_html":"<p>by <a href=\"https://flamerix.ai\" rel=\"nofollow ugc noopener\">Flamerix AI</a> · MIT License</p>\n<blockquote><p>Tested on Linux, Claude Code 2.1.257–2.1.312 (see Tested versions). The binary is not modified (<code>BUN_OPTIONS=--preload</code>). An update can silently undo it, so see Verify.</p></blockquote>\n<h2 id=\"tl-dr\">TL;DR</h2>\n<pre><code>curl -fsSL https://gist.githubusercontent.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce/raw/no-agent-summary.js -o ~/.local/share/no-agent-summary.js\nclaude daemon stop --any\nBUN_OPTIONS=&quot;${BUN_OPTIONS:+$BUN_OPTIONS }--preload $HOME/.local/share/no-agent-summary.js&quot; claude</code></pre>\n<p>To make it permanent for every Claude Code process, see <a href=\"#install\">Install</a>.</p>\n<h2 id=\"the-problem\">The problem</h2>\n<p>Every ~30 seconds, for every background agent, Claude Code sends that agent&#39;s <strong>entire\ntranscript</strong> to the agent&#39;s own model (usually Opus) and asks it to &quot;describe your most recent\naction in 3-5 words&quot;. The answer only updates the agent&#39;s status line in the agents panel; it\nnever reaches the model or your conversation. There is no setting that turns it off, and since\n2.1.232 (August 2026) it has been on by default.</p>\n<p>The anomaly was detected by <a href=\"https://flamerix.ai\" rel=\"nofollow ugc noopener\">DRIM</a> in Claude Code&#39;s OpenTelemetry export:\ngrouping <code>claude_code.api_request</code> events by <code>query_source</code> showed <code>agent_summary</code> costing <strong>more than the main conversation itself</strong>.</p>\n<div class=\"table-wrap\"><table><thead><tr><th><code>query_source = agent_summary</code></th><th>week before the hook</th><th>2 days after</th></tr></thead><tbody><tr><td>share of all Claude Code spend</td><td><strong>16.4 %</strong> (main conversation: 15.7 %)</td><td><strong>0 %</strong></td></tr><tr><td>share of all API requests</td><td><strong>19.7 %</strong>, about 1 in 5</td><td><strong>0 %</strong></td></tr><tr><td>part of that spend on Opus</td><td>96 %</td><td>—</td></tr><tr><td>input tokens per output token</td><td>≈ 9,000 : 1</td><td>—</td></tr></tbody></table></div>\n<p>Spend here means Claude Code&#39;s own per-request estimate (<code>cost_usd</code>), not an invoice: on a\nsubscription, what you lose is rate-limit headroom. We are not the only ones who measured it:\n<a href=\"https://github.com/anthropics/claude-code/issues/86879\" rel=\"nofollow ugc noopener\">#86879</a> collects four independent\nmeasurements of 18–26 %.</p>\n<h2 id=\"the-fix-how-it-works\">The fix (how it works)</h2>\n<p><code>no-agent-summary.js</code> is a Bun preload (~80 lines of code). It wraps <code>setTimeout</code>/<code>setInterval</code>;\nwhen a callback&#39;s source text contains a summarizer anchor (e.g. the <code>[AgentSummary] Timer fired</code>\nlog line), it returns an inert handle instead of arming the timer. Every other timer passes\nthrough untouched. In the OpenTelemetry export the suppressed requests are\n<code>claude_code.api_request</code> events with <code>query_source = agent_summary</code>.</p>\n<p>Why a timer hook rather than something simpler: the module ships as precompiled Bun bytecode, so\nediting its JS text inside the binary changes nothing, and the API client does not go through a\nwrapped <code>globalThis.fetch</code>. But <code>Function.prototype.toString</code> still returns a callback&#39;s source\neven when it runs from bytecode, so the timer can be recognised by a string inside it. The inert\ntimer uses exactly 2³¹−1 ms: Node and Bun treat a larger delay as 1 ms, so an &quot;infinite&quot; delay\nwould fire at once.</p>\n<h2 id=\"install\">Install</h2>\n<ol><li>Put <code>no-agent-summary.js</code> somewhere stable; note its <strong>absolute</strong> path.</li><li><p>Set <code>BUN_OPTIONS</code> <strong>globally</strong> so every Claude Code process gets it. One of:</p><p> <code>\n # ~/.profile, ~/.bashrc, ~/.zshrc\n export BUN_OPTIONS=&quot;${BUN_OPTIONS:+$BUN_OPTIONS }--preload /abs/path/no-agent-summary.js&quot;\n </code></p>\n<p> <code>\n # ~/.config/fish/config.fish\n set -gx BUN_OPTIONS &quot;--preload /abs/path/no-agent-summary.js&quot;\n </code></p>\n<p> <code>\n # ~/.config/environment.d/50-no-agent-summary.conf (systemd user env; also reaches\n # user services and timers; applies when the user manager next starts, usually next login)\n BUN_OPTIONS=--preload /abs/path/no-agent-summary.js\n </code></p>\n<p> Or a wrapper named <code>claude</code> earlier on <code>PATH</code> (the <code>case</code> keeps it idempotent when one Claude\n Code process starts another):</p>\n<p> <code>\n #!/bin/sh\n HOOK=/abs/path/no-agent-summary.js\n case &quot; ${BUN_OPTIONS-} &quot; in\n   *&quot; --preload $HOOK &quot;*) ;;\n   *) export BUN_OPTIONS=&quot;${BUN_OPTIONS:+$BUN_OPTIONS }--preload $HOOK&quot; ;;\n esac\n : &quot;${CC_AGENT_SUMMARY_LOG:=${XDG_RUNTIME_DIR:-/tmp}/no-agent-summary.log}&quot;\n export CC_AGENT_SUMMARY_LOG\n exec /path/to/the/real/claude &quot;$@&quot;\n </code></p></li><li><p>Restart the daemon and every already-running session, then start Claude Code from an</p><p> environment that has <code>BUN_OPTIONS</code>:</p>\n<p> <code>\n claude daemon stop --any\n </code></p></li></ol>\n<h3 id=\"env\">Env</h3>\n<div class=\"table-wrap\"><table><thead><tr><th>variable</th><th>values</th><th>effect</th></tr></thead><tbody><tr><td><code>CC_AGENT_SUMMARY_MODE</code></td><td><code>off</code> (default), <code>pass</code></td><td><code>off</code> suppresses the summary timer. <code>pass</code> is the A/B control: loads and logs, arms every timer untouched. Any other value warns on stderr and acts as <code>off</code>.</td></tr><tr><td><code>CC_AGENT_SUMMARY_LOG</code></td><td>file path, optional</td><td>Appends <code>preload active mode=… bun=… hooked=…</code> on load, one line per matched timer (first 20), and an exit count.</td></tr><tr><td><code>CC_AGENT_SUMMARY_MARKER</code></td><td>file path, optional</td><td>Overwritten on load with the <code>preload active …</code> line.</td></tr></tbody></table></div>\n<h2 id=\"nuances\">Nuances</h2>\n<ul><li><p><strong>Restart the daemon after install.</strong> The daemon, pty host and pre-warmed <code>bg-spare</code> sessions</p><p>inherit the environment of the client that started the daemon, so a daemon started before\n<code>BUN_OPTIONS</code> was set keeps spawning unhooked sessions. Hence <code>claude daemon stop --any</code>.</p></li><li><strong>Headless runs need it too:</strong> <code>claude -p</code>, cron, CI, editor integrations.</li><li><p><strong>Platform:</strong> Linux tested, macOS and Windows untested. On Windows the <code>/$bunfs/</code> gate most</p><p>likely never installs the hook.</p></li><li><strong>A/B:</strong> <code>CC_AGENT_SUMMARY_MODE=pass</code> loads the hook but arms every timer; the summaries come back, which shows the <code>off</code> result is the hook&#39;s doing.</li><li><strong>Background processes:</strong> daemon, pty host and <code>bg-spare</code> must carry <code>--preload</code> in their environment (<code>/proc/&lt;pid&gt;/environ</code> on Linux); if not, restart the daemon.</li></ul>\n<h2 id=\"verify-after-every-update\">Verify (after every update)</h2>\n<ol><li><p><strong>Preload loads:</strong></p><p> <code>\n BUN_OPTIONS=&quot;--preload $HOME/.local/share/no-agent-summary.js&quot; CC_AGENT_SUMMARY_LOG=/tmp/nas.log claude --version &amp;&amp; cat /tmp/nas.log\n </code></p>\n<p> Expect <code>preload active mode=off …</code>. No line: <code>BUN_OPTIONS</code> did not reach the process, or the\n path is not absolute.</p></li><li><p><strong>Timer suppressed:</strong> with the same <code>BUN_OPTIONS</code> and <code>CC_AGENT_SUMMARY_LOG=/tmp/nas.log</code>, run</p><p> a background subagent for a minute or two. The log shows\n <code>summary timer #1 suppressed (setTimeout, delay=30000)</code> and the agent&#39;s panel status stops\n updating. If you export OpenTelemetry, <code>query_source = agent_summary</code> should stay at zero; this\n is the only check that catches every failure mode.</p></li></ol>\n<h3 id=\"fail-open\">Fail-open</h3>\n<p>Matching is by <strong>source text</strong>: an anchor must sit inside the callback passed to the timer. If an\nupdate breaks that, the hook matches nothing and summaries silently come back. Ways it can happen:</p>\n<ul><li>the log line or query-source id is renamed;</li><li>the callback is passed via <code>fn.bind(...)</code> (no source in <code>toString</code>) or another wrapper;</li><li>the strings move to a shared constant outside the callback;</li><li>the timer moves to an unwrapped API (<code>timers/promises</code>, a scheduler loop).</li></ul>\n<p>So re-run Verify after every update.</p>\n<h2 id=\"effect\">Effect</h2>\n<ul><li><p>We run it under every Claude Code process on our machine (2.1.301). Since then there have been</p><p>zero <code>agent_summary</code> requests, and no other side query grew to take its place.</p></li><li><p>Subagents, background agents, forked skills and background sessions run normally; only the</p><p>summary fork is skipped.</p></li><li>Visible change: the per-agent panel status stops updating; SDK <code>task_progress</code> has no <code>summary</code>.</li><li><p>Overhead: one <code>Function.prototype.toString</code> per distinct timer callback, cached in a <code>WeakMap</code>.</p><p><code>claude --version</code>: ~10 ms → ~17 ms (one measurement).</p></li></ul>\n<h2 id=\"tested-versions\">Tested versions</h2>\n<p>Linux x64 binary from npm <code>@anthropic-ai/claude-code-linux-x64</code> (its sha256 equals the native\ninstaller&#39;s <code>manifest.json</code> checksum). <strong>Static:</strong> the extracted bundle&#39;s summary timer callback\ncarries a hook anchor. <strong>Live:</strong> <code>claude -p --model haiku</code> with\n<code>CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES=1</code>, one subagent doing 8 steps of ~20 s, counting\n<code>[AgentSummary] Forking</code> in <code>--debug-file</code>.</p>\n<div class=\"table-wrap\"><table><thead><tr><th>version</th><th>published</th><th>sha256</th><th>static</th><th>summary forks in a ~2.5-min agent run: no hook / hook <code>pass</code> / hook <code>off</code></th><th>notes</th></tr></thead><tbody><tr><td>2.1.312</td><td>2026-10-27</td><td><code>4f4178ce41d8</code></td><td>OK</td><td><strong>5 / 5 / 0</strong></td><td><code>latest</code> on 2026-10-28; interactive: 6 no hook, 0 <code>off</code></td></tr><tr><td>2.1.310</td><td>2026-10-23</td><td><code>b73676430af1</code></td><td>OK</td><td><strong>5 / 5 / 0</strong></td><td></td></tr><tr><td>2.1.308</td><td>2026-10-22</td><td><code>356733f185fa</code></td><td>OK</td><td><strong>5 / 6 / 0</strong></td><td><code>stable</code> on 2026-10-28; the <code>pass</code> run&#39;s agent took 9 steps instead of 8</td></tr><tr><td>2.1.301</td><td>2026-10-15</td><td><code>bc1aad9982c7</code></td><td>OK</td><td><strong>5 / 5 / 0</strong></td><td>same with earlier hook builds; interactive <code>off</code> also 0; in daily use since 2026-10-26, 0 <code>agent_summary</code> requests in telemetry</td></tr><tr><td>2.1.280</td><td>2026-09-22</td><td><code>1e08503dbdf3</code></td><td>OK</td><td><strong>1 / 1 / 0</strong> (timer fired 11 / 11 / 0)</td><td>interactive: <code>-p</code> cannot enable summaries on this version. The agent sat at a permission prompt, so later timers skipped the unchanged transcript</td></tr><tr><td>2.1.257</td><td>2026-09-01</td><td><code>9a64bda9d872</code></td><td>OK</td><td><strong>1 / 1 / 0</strong> (timer fired 11 / 11 / 0)</td><td>same as 2.1.280</td></tr></tbody></table></div>\n<p>5 / 5 / 0 = the fork happens on this version, merely loading the hook changes nothing, and <code>off</code>\nremoves it.</p>\n<h2 id=\"why-there-is-no-setting-for-this\">Why there is no setting for this</h2>\n<ul><li><p><strong>The documented switch only turns it on.</strong> The SDK option <code>agentProgressSummaries</code> defaults to</p><p><code>false</code>, but in the bundle it is one term of an OR, and every code path that writes it sets\n<code>true</code>. So <code>false</code> changes nothing, and the undocumented <code>CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES</code>\ncan only enable summaries too.</p></li><li><p><strong>The interval belongs to the server.</strong> It comes from a per-model-family server flag and is</p><p>clamped to 30–240 s. The default is already the minimum, and no value disables the timer.</p></li><li><p><strong>The obvious env vars miss.</strong> <code>DISABLE_NON_ESSENTIAL_MODEL_CALLS</code>, which some packagers set,</p><p>does not occur in the binary at all. <code>CLAUDE_CODE_FORK_SUBAGENT=0</code> and\n<code>CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1</code> do stop it, but only by taking fork subagents or\nbackground agents with it.</p></li><li><p><strong>The prompt cache makes it cheaper, not cheap.</strong> About 98 % of these calls re-read the</p><p>transcript from cache; the ~2 % that hit a cold cache carry over a third of its cost.</p></li><li><p><strong>Cost tools that read transcripts don&#39;t see it.</strong> These requests are not written to the</p><p>session logs on disk (<a href=\"https://github.com/anthropics/claude-code/issues/104358\" rel=\"nofollow ugc noopener\">#104358</a>); only\nthe OpenTelemetry export shows them. The docs section on background token usage doesn&#39;t list it\neither, while Agent View&#39;s row summaries are documented as a Haiku-class model refreshing every\nfew minutes.</p></li><li><p><strong>A cheaper variant already exists, switched off.</strong> 2.1.310 ships a <code>short</code> mode: about 2k</p><p>tokens of notes instead of the full transcript, optionally written by a smaller model. The\ndefault is still the full transcript, and only server flags can change it.</p></li></ul>\n<h3 id=\"timeline-2026\">Timeline (2026)</h3>\n<div class=\"table-wrap\"><table><thead><tr><th>version (published)</th><th>what changed</th></tr></thead><tbody><tr><td>2.1.32 (Feb 5)</td><td>the code first appears; dead in public builds</td></tr><tr><td>2.1.72 (Mar 9)</td><td>reachable as an SDK opt-in</td></tr><tr><td>2.1.117 (Apr 21)</td><td>reachable in the interactive CLI behind the fork gate or a server rollout</td></tr><tr><td>≤ 2.1.220 (Jul 24)</td><td>forked skills are always summarized</td></tr><tr><td><strong>2.1.232 (Aug 13)</strong></td><td><strong>on by default</strong> for interactive background agents</td></tr><tr><td>2.1.300 (Oct 14)</td><td>interval becomes server-tunable, 30–240 s</td></tr><tr><td>2.1.310 (Oct 23)</td><td>the cheaper <code>short</code> mode lands, switched off</td></tr></tbody></table></div>\n<p>In our telemetry its weekly share of spend went from 3.5–7 % in July (the fork gate was already\nopen for us) to 7.3 % and then 11.8 % in the week we picked up 2.1.233, and to about 16 % by\nmid-October.</p>\n<h2 id=\"license-and-disclaimer\">License and disclaimer</h2>\n<p>MIT © 2026 <a href=\"https://flamerix.ai\" rel=\"nofollow ugc noopener\">Flamerix AI</a>. Provided <strong>as is</strong>, without warranty of any kind;\nyou use it at your own risk. Flamerix AI is not liable for any damage, data loss or account\naction arising from its use. Changing a client&#39;s runtime behaviour may conflict with the terms of\nthe service you use it with; checking that is on you.</p>\n<p>&quot;Claude&quot; and &quot;Claude Code&quot; are trademarks of Anthropic, PBC. This project is independent and is\nnot affiliated with, sponsored or endorsed by Anthropic.</p>","headings":[{"level":2,"text":"TL;DR","id":"tl-dr"},{"level":2,"text":"The problem","id":"the-problem"},{"level":2,"text":"The fix (how it works)","id":"the-fix-how-it-works"},{"level":2,"text":"Install","id":"install"},{"level":3,"text":"Env","id":"env"},{"level":2,"text":"Nuances","id":"nuances"},{"level":2,"text":"Verify (after every update)","id":"verify-after-every-update"},{"level":3,"text":"Fail-open","id":"fail-open"},{"level":2,"text":"Effect","id":"effect"},{"level":2,"text":"Tested versions","id":"tested-versions"},{"level":2,"text":"Why there is no setting for this","id":"why-there-is-no-setting-for-this"},{"level":3,"text":"Timeline (2026)","id":"timeline-2026"},{"level":2,"text":"License and disclaimer","id":"license-and-disclaimer"}]}}