[Bug] Subagent polling wake-ups consume excessive tokens for no-op status checkssu
Bug Description
Monitor-based subagent polling burns ~150k+ tokens per 30s wake for a no-op status check
▎ A background subagent set up a 30-second polling loop to wait on a long-running git push (pre-push hook running full CI). Each
▎ poll tick woke the subagent's full turn and cost ~146k–160k tokens just to report "still running" — no new work done. Over ~5
▎ minutes of wall-clock time this fired 12+ times, costing an estimated 1.8M+ tokens in pure polling overhead for one background
▎ command. Evidence: task id a8e16d4b621fa3ffb, 12 consecutive notifications with subagent_tokens in the 146k–160k range and
▎ tool_uses: 122 unchanged across several — suggesting the same large context was reprocessed each wake rather than a lightweight
▎ check.
Environment Info
- Platform: darwin
- Terminal: vscode
- Version: 2.1.247
- Feedback ID: 8263f9a5-d755-43db-b7da-34d6d413c914
Errors
[]
4 Comments
This is a real cost problem for anyone running long background commands through subagents. The pattern you're describing - full context reprocessed on every progress notification - means the token cost of monitoring scales with context size rather than with actual work done.
A few things that might help while this gets fixed:
The most direct workaround is to have the subagent exit rather than poll. Instead of keeping the subagent alive to monitor the background task, spawn it with a completion artifact contract: the orchestrating agent checks for the artifact file on a slow polling loop rather than leaving a live subagent waiting. No live context means no per-notification reprocess.
If you need the subagent to stay alive for immediate handling of results, setting a hard limit on the maximum wake count (via the subagent's instructions: "check at most N times, then report status and exit") caps the worst case. Not elegant but it gives you a ceiling.
The fundamental fix is what you're asking for: lightweight status-check wakes that don't reload the full context. The current behavior of treating every progress notification as a new turn with full context is the inefficiency.
One diagnostic question: does the 146k-160k token range stay roughly constant across all 12 wakes, or does it grow? Constant suggests a fixed context reload on each wake. Growing would suggest progress notifications are being accumulated in context, which is a different (worse) problem.
This is a great example of an LLM call that probably shouldn’t be an LLM call at all.
A polling operation whose only job is effectively “has the state changed?” should ideally stay deterministic until there is new information worth giving back to the model.
It would also be useful to expose this at the request level: source=subagent_poll, context tokens, new tokens introduced, and estimated cost. Then cases where 1.8M tokens were spent while producing effectively zero new work become immediately visible.
We’re building ModelGate around a similar idea: not just measuring LLM spend, but finding repetitive and expensive LLM traffic that may not need to stay an LLM call.
This would be a very good real-world example of that distinction.
30-second poll ticks that reprocess 150k+ tokens just to say "still running" are pure overhead.
wake on completion events, not a clock — or accept that the meter is the product of your wait loop.
30-second poll ticks that reprocess huge contexts just to say still running are pure overhead — event-driven wake beats re-feeding the whole window.