{"slug": "fork-of-flamerix-ai-s-no-agent-summary-claude-code-agent-summary-off-switch-v3-1", "title": "Fork of flamerix-ai's no-agent-summary (Claude Code agent_summary off switch), v3.1: same blocking code, plus a per-build self-check that warns instead of failing silently, a session-start check,…", "summary": "A developer published v3.1 of a fork of flamerix-ai's no-agent-summary hook for Claude Code, keeping the upstream code that blocks agent summaries unchanged while adding a per-build self-check that warns inside the session instead of failing silently when a Claude Code update breaks the hook's binary anchors. The fork also adds a session-start check, status.sh for listing which running Claude Code processes are hooked, and an install.sh that writes the hook, shell config and settings.json entries and can uninstall them byte-identically. Testing showed the hook cut summary calls during a ~175 s subagent run from 5 to 0, while disableAgentView: true still produced 5 calls and CLAUDE_CODE_FORK_SUBAGENT=0 stopped them only by disabling fork subagents entirely.", "body_md": "Fork of [flamerix-ai's gist](https://gist.github.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce) (v3).\n**The code that blocks the summaries is upstream's, unchanged.** This fork adds what makes it safe to leave\ninstalled across Claude Code updates. Upstream's full README follows the line below, unchanged.\n\n- **Fails loudly, not silently, inside the session.** Once per Claude Code build the hook scans the binary for the\nanchors it matches on (~50 ms on a 276 MB build, cached by path + size + mtime) and records the verdict. A\nsession-start check (`check.sh` , added to Claude Code's`settings.json` by the installer) then shows, inside\nthe session,`agent-summary blocker: this Claude Code build (…) no longer contains the code it blocks` when an\nupdate broke the match, or`agent-summary blocker: NOT loaded in this session` when the session was started\nwithout`BUN_OPTIONS` . It is silent when all is well. (The hook's own stderr line is not enough: tested in an\ninteractive session, Claude Code's full-screen UI draws over it and it only reappears after exit.)\n- **Proof it caught one.** The first suppressed summary timer on each build is recorded, so you can see the hook\ndid its job on the version you run, not only that it loaded.\n- **`status.sh`** lists every build checked (anchors present, suppression seen) and every running Claude Code\nprocess as`hooked` or`NOT hooked (restart it)` . Each hooked process leaves a marker while it runs, because\nmacOS does not expose another process's environment.\n- **`install.sh`** copies the hook to`~/.local/share/no-agent-summary/` (a path without spaces, since`BUN_OPTIONS` splits on whitespace), adds one idempotent block to`~/.zshrc` or`~/.bashrc` , adds the session-start check to`$CLAUDE_CONFIG_DIR/settings.json` (default`~/.claude` ; existing hooks kept, previous file saved as`settings.json.nas-bak` ; needs`python3` ), and checks that`claude --version` loads the hook.`install.sh --uninstall` removes all three (tested: settings and rc file come back byte-identical).\n\n```\ngit clone https://gist.github.com/c4511b8642c9d7179ab5761ff6473871.git no-agent-summary && bash no-agent-summary/install.sh\n```\n\nRestart running sessions afterwards; `status.sh` names the ones still unhooked.\n\nSummary calls during one ~175 s subagent run, counted from `[AgentSummary] Forking` in `--debug-file`:\n\n| session | how summaries were on | no hook | hook `pass` | hook `off` | \n|---|---|---|---|---|\n| headless ( `claude -p --model haiku` ) | `CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES=1` | 5 | 5 (v3) | 0 (v3, and v3.1) | \n| interactive TUI, background agent | default, nothing set | n/a | 5 (v3.1) | 0 (v3.1) | \n\n- The session-start check, in real interactive sessions: a deliberately broken anchor showed the \"no longer\ncontains\" message; a session started without `BUN_OPTIONS` showed \"NOT loaded\"; a normal hooked session showed nothing.\n- Two settings people suggest instead, tested the same way with no hook: `disableAgentView: true` → still 5 calls\n(it only hides the agents screen);`\"env\": {\"CLAUDE_CODE_FORK_SUBAGENT\": \"0\"}` → 0 calls, but it also turns off\nfork subagents. If you never use forks, that one line is the simplest fix.\n- Each summary call re-read the subagent's whole context (median 37,250 tokens, from cache) to return a 14-token label (10 calls).\n- Upstream's matching costs almost nothing: 2.2 ms over 4.8 minutes of an interactive session (4,566 `setTimeout` calls, 3,305 callbacks stringified). So it is left as is.\n- The gate admits the macOS native path (`~/.local/share/claude/versions/<ver>` ) and the desktop app's bundled\ncopy (`…/Claude/claude-code/<ver>/<hash>/claude.app/Contents/MacOS/claude` ;`Bun.main` is`/$bunfs/root/cli` in both). Agents were not run in the desktop app.\n- Size on one user's machine: replaying the 30-s timer over a week of transcripts (190 background agents, mostly Opus 5.5) puts summaries at about 8% of list-price-equivalent spend. The replay was checked against a live run (5 calls predicted, 5 measured). Lower than upstream's 16%, partly because Opus 5.5 cache reads cost 0.05× input and the replay assumes a warm cache.\n\nby [Flamerix AI](https://flamerix.ai) · MIT License\n\nTested on Linux, Claude Code 2.1.257–2.1.312; macOS reported working by others (see Tested versions). The binary is not modified (`BUN_OPTIONS=--preload`). An update can silently undo it, so see Verify.\n\nEvery ~30 seconds, Claude Code re-sends each background agent's full transcript to the model just\nto fill that agent's status line in the agents panel. `no-agent-summary.js` is a Bun `--preload`\nthat stops that timer without modifying the Claude Code binary.\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\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`no-agent-summary.js` is a Bun preload (~70 lines of code). It wraps only the global\n`setTimeout`, the one the summary timer is armed through; when a callback's source text contains\na 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\nThe hook installs only inside Claude Code: `Bun.main` must be `/$bunfs/root/cli` (the compiled\nentry, the same on every row below) and `process.execPath` must be a Claude Code install. Any other\n`bun`, or a compiled Bun binary with another entry name, loads the file and does nothing.\n\n| install channel (Linux, measured with `claude --version` ) | `process.execPath` | \n|---|---|\n| nix | `…/bin/.claude-wrapped` | \n| npm `@anthropic-ai/claude-code` | `…/@anthropic-ai/claude-code/bin/claude.exe` (`.exe` on Linux too) | \n| npm platform package | `…/@anthropic-ai/claude-code-linux-x64/claude` | \n| native installer | `~/.local/share/claude/versions/<version>` | \n\nmacOS and Windows are not measured; the hook installs there only if they produce these same\nshapes, and Verify step 1 tells you. One known pass-through: a foreign binary built with\n`bun build --compile --outfile cli` and renamed `claude`; there, only timers whose callback\ncarries an anchor change.\n\n1. \nPut `no-agent-summary.js` somewhere stable; note its**absolute** path.\n2. \nSet `BUN_OPTIONS`**globally** so every Claude Code process gets it. Write`--preload=<path>` , with`=` :`BUN_OPTIONS` reaches every`bun` on the machine, and with a\nspace (`--preload <path>` ) bun subcommands break (`bun install` →`error: Script not found \"install\"` ). Claude Code reads both forms. One of:\n\n```\n# ~/.profile, ~/.bashrc, ~/.zshrc\nexport BUN_OPTIONS=\"${BUN_OPTIONS:+$BUN_OPTIONS }--preload=/abs/path/no-agent-summary.js\"\n# ~/.config/fish/config.fish\nset -gx BUN_OPTIONS \"--preload=/abs/path/no-agent-summary.js\"\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)\nBUN_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\nCode process starts another, and also recognises the v2 space form):\n\n``` bash\n#!/bin/sh\nHOOK=/abs/path/no-agent-summary.js\ncase \" ${BUN_OPTIONS-} \" in\n  *\" --preload=$HOOK \"*|*\" --preload $HOOK \"*) ;;\n  *) export BUN_OPTIONS=\"${BUN_OPTIONS:+$BUN_OPTIONS }--preload=$HOOK\" ;;\nesac\n: \"${CC_AGENT_SUMMARY_LOG:=${XDG_RUNTIME_DIR:-/tmp}/no-agent-summary.log}\"\nexport CC_AGENT_SUMMARY_LOG\nexec /path/to/the/real/claude \"$@\"\n```\n\n3. \nRestart the daemon and every already-running session, then start Claude Code from an environment that has `BUN_OPTIONS` :\n\n```\nclaude daemon stop --any\n```\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=global.setTimeout` 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- **Restart the daemon after install.** The daemon, pty host and pre-warmed`bg-spare` sessions\ninherit the environment of the client that started the daemon, so a daemon started before`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 by us, macOS (darwin-arm64) reported working by others, Windows untested (see[Which processes it touches](#which-processes-it-touches) ).\n- **npm `--ignore-scripts`:**` bin/claude` stays a shell stub and Bun never starts, so the hook\nnever runs.\n- **Upgrading from v2:** overwrite the file in place, switch`BUN_OPTIONS` to the`=` form and\nrestart the daemon. If both forms name the same path, bun loads it once. Two different paths\n(say an old copy kept beside the new one) both load: a second v3 copy is a no-op, but v2 has no\nsuch guard, so do not leave a v2 path in`BUN_OPTIONS` .\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\n1. \n**Preload loads:**\n\n```\nBUN_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\npath is not absolute.\n2. \n**Timer suppressed:** with the same`BUN_OPTIONS` and`CC_AGENT_SUMMARY_LOG=/tmp/nas.log` , run\na background subagent for a minute or two. The log shows`summary timer #1 suppressed (setTimeout, delay=30000)` and the agent's panel status stops\nupdating. If you export OpenTelemetry,`query_source = agent_summary` should stay at zero; this\nis the only check that catches every failure mode.\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 off the global `setTimeout` (`setInterval` ,`require(\"timers\")` ,`timers/promises` , a scheduler loop).\n\nSo re-run Verify after every update.\n\n- We run it under every Claude Code process on our machine (2.1.301). Since then there have been\nzero `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 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` .`claude --version` : ~10 ms → ~17 ms (one measurement).\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\nThe two **v3** rows are this script, re-run on 2026-10-29. The rows below them were measured with v2.\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 **v3** | 2026-10-27 | `4f4178ce41d8` | OK | **5 / 6 / 0** | the `pass` run took 232 s on a loaded machine, so one more 30 s tick fit;`off` with the gist file itself: 0 | \n| 2.1.301 **v3** | 2026-10-15 | `bc1aad9982c7` | OK | **5 / 5 / 0** | run through the Nix-packaged binary rather than the npm one | \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**Reported by others**\n\n| platform | version | checked | by | \n|---|---|---|---|\n| macOS 26.7.1, native darwin-arm64, bun 1.4.4 | 2.1.312 | static: the 3–5 words prompt, the timer (30 s, clamped 30–240 s), no off switch; live: the preload loads, and an interactive session with a background subagent logged `summary timer #1 suppressed (setTimeout, delay=30000)` . Token numbers not checked (no OTEL export) | [lostsock](https://news.ycombinator.com/user?id=lostsock) , HN, 2026-10-29 | \n\nOn `claude -p`: headless runs don't arm the summary timer by default. Our live tests set\n`CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES=1` for that reason. The default-on overhead is in interactive\nsessions with background agents.\n\n- **The documented switch only turns it on.** The SDK option`agentProgressSummaries` defaults to`false` , but in the bundle it is one term of an OR, and every code path that writes it sets`true` . So`false` changes nothing, and the undocumented`CLAUDE_CODE_AGENT_PROGRESS_SUMMARIES` can only enable summaries too.\n- **The interval belongs to the server.** It comes from a per-model-family server flag and is\nclamped 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,\ndoes not occur in the binary at all.`CLAUDE_CODE_FORK_SUBAGENT=0` and`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` do stop it, but only by taking fork subagents or\nbackground agents with it.\n- **The prompt cache makes it cheaper, not cheap.** About 98 % of these calls re-read the\ntranscript 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\nsession logs on disk ([#104358](https://github.com/anthropics/claude-code/issues/104358) ); only\nthe OpenTelemetry export shows them. The docs section on background token usage doesn't list it\neither, while Agent View's row summaries are documented as a Haiku-class model refreshing every\nfew minutes.\n- **A cheaper variant already exists, switched off.** 2.1.310 ships a`short` mode: about 2k\ntokens 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.\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 open for us) to 7.3 % and then 11.8 % in the week we picked up 2.1.233, and to about 16 % by mid-October.\n\n- **docs** — first macOS report (darwin-arm64, 2.1.312) added to[Tested versions](#tested-versions) . Thanks to[lostsock](https://news.ycombinator.com/user?id=lostsock) .\n- **v3** —`--preload=<path>` everywhere (the space form broke bun subcommands); the hook installs\nonly inside Claude Code (`Bun.main` plus`process.execPath` , see[Which processes it touches](#which-processes-it-touches) ); a second load in the same process is\na no-op. Thanks to[Bedz01](https://gist.github.com/Bedz01) for the`--preload=` form and the\nClaude-Code-only gate (gist comment, 2026-10-29).\n- **v3** — wraps only the global`setTimeout` , the one the summary timer is armed through (v2\nalso wrapped`setInterval` and`require(\"timers\")` ).\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 not affiliated with, sponsored or endorsed by Anthropic.", "url": "https://wpnews.pro/news/fork-of-flamerix-ai-s-no-agent-summary-claude-code-agent-summary-off-switch-v3-1", "canonical_source": "https://gist.github.com/declanbx/c4511b8642c9d7179ab5761ff6473871", "published_at": "2026-10-30 00:03:12+00:00", "updated_at": "2026-10-30 13:17:19.782511+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["Claude Code", "Anthropic", "flamerix-ai", "Bun"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/fork-of-flamerix-ai-s-no-agent-summary-claude-code-agent-summary-off-switch-v3-1", "markdown": "https://wpnews.pro/news/fork-of-flamerix-ai-s-no-agent-summary-claude-code-agent-summary-off-switch-v3-1.md", "text": "https://wpnews.pro/news/fork-of-flamerix-ai-s-no-agent-summary-claude-code-agent-summary-off-switch-v3-1.txt", "jsonld": "https://wpnews.pro/news/fork-of-flamerix-ai-s-no-agent-summary-claude-code-agent-summary-off-switch-v3-1.jsonld"}}