[BUG] VSCode: background agent output streams into foreground chat, disrupting active conversation
Preflight
- [x] I have searched existing issues and this specific angle isn't reported
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
Summary
In the VSCode extension, when Claude spawns subagents with run_in_background: true (or when fork mode is active and all agents become background-by-default), the agent's complete output — all tool calls, all intermediate text responses, all reasoning — streams directly into the main foreground chat window. This defeats the stated purpose of background execution.
Additionally, when multiple background agents run simultaneously, their outputs intermingle with each other AND with the actual conversation in the chat window, making it very difficult to follow any of them or the foreground conversation.
Environment
- Claude Code VSCode Extension: v2.1.159
- Platform: macOS (Darwin 24.6.0)
- Shell: bash
- Models affected: claude-opus-4-6 (1M context), claude-opus-4-8 (1M context)
Expected behavior
Background agents should run concurrently while the user can continue working in the foreground chat. Per the documentation:
"Background subagents run concurrently while you continue working." "The fork appears in a panel below your prompt and runs in the background while you keep working."
In the terminal/TUI, this is implemented as a separate panel below the prompt input. The user can keep reading and responding in the main chat while the agent works separately.
Actual behavior
In the VSCode extension, there is no equivalent of the terminal's "panel below the prompt." Instead:
- ALL background agent output — tool call descriptions, intermediate text, every response turn — streams directly into the main foreground chat window, inline with the user's conversation.
- Multiple simultaneous background agents intermingle their output with each other and with the actual conversation, creating an unreadable jumble.
- The user cannot read previous messages while agents are running (the chat is being actively modified/scrolled by background agent output).
- The background/foreground distinction is meaningless from the user's perspective — a background agent is experientially identical to a foreground agent in VSCode.
Distinction from #9512 and #11950
Those issues are about subagent verbosity (showing tool call details vs. summaries). Both were closed as "not planned." This issue is different: the background vs. foreground separation is entirely absent in VSCode. Even a perfectly-summarized background agent that only produced a one-line result would still write that result into the main chat mid-conversation, interrupting whatever the user was reading.
The verbosity is a secondary concern. The primary concern is that background agents have no separate display space in VSCode and therefore cannot be background in any meaningful sense.
Related issues
- #54595 — Fork mode silently removed
run_in_backgroundfrom tool schema, making all agents background-by-default. Under fork mode, this display issue affects every single agent spawn. - #9512 — Subagent output control settings (closed as not planned) — general verbosity, different problem
- #11950 — VSCode: hide subagent internals (closed as not planned) — similar complaint but framed as verbosity
- #60095 — Background task chips persist as Running — related background agent UI issues
Suggested fix directions
- Display background agent output in a collapsible section in the VSCode chat (collapsed by default, expandable for inspection)
- Show only a chip/status indicator while the agent runs; reveal output on click/expand
- Route background agent output to a dedicated VSCode panel (e.g., "Claude Agents" sidebar view) rather than the main chat
- At minimum, visually separate background agent output from the foreground conversation so intermingled output from multiple agents + conversation is distinguishable
- Revert to original behavior of simply logging all background agent output to a log file instead of the updated behavior of all agent output going to the log file AND directly into the chat window. While 1-4 are better options (really would be nice to have agent output captured & easily accessible), simply reverting to the prior behavior would result in background agents actually allowing the user to continue working instead of completely derailing work until all background agents complete.
9 Comments
Additional information after testing with version 2.1.160:
This is the crux of the recent behavior change. Tool calls for background agents in the past seemed to occasionally log a tool call description in the chat window but the actual agent OUTPUT did NOT just get dumped directly into the chat window.
Confirmed that a foreground agent is still a foreground agent and is blocking Claude (and the user) from continuing until the agent returns. Confirmed that background agents are actually in the background, allowing Claude to continue working while the agent works. Unfortunately, while Claude can continue while the background agent works, the same can NOT be said for the user. Output from the primary Claude instance and any background agent(s) intermingle in the chat window, making it difficult or impossible for the user to follow
+1
It becomes impossible to tell which feedback comes form the orchestrator and which from the sub-agent. Aslo impossible to hold a conversation because content keeps drifting off screen.
2.1.163, same behavior, but testing confirmed that the flooding from the background agent occurs only after the first tool call. Any agent response prior to the first tool call is NOT sent to the main chat window. The first tool call seemed to trigger a very brief flash of that response text that immediately disappeared and was replaced with the tool call stub. From that point on all agent response text and tool calls get dumped into the main chat window.
fwiw, my Claude suggested to use Workflow until Agent is fixed. probably an overkill but at least no noisy output is leaking from background tasks.
Downgrading Claude Code for VS Code to v2.1.145 helped me.
Reproduces in the VS Code extension with a slightly different code path that may be worth noting:
Use case: a custom slash command that fans out 3 foreground subagents in parallel via the Agent tool (no
run_in_background, and the subagents themselves call zero tools — they just return text opinions for a judge-panel pattern).Observed: each subagent's response text shows up in the main foreground chat, drowning out the parent conversation. Even an opt-in design choice like collapsed-by-default cards (similar to thinking blocks) would already make iterative/judge-panel skills usable again in the extension.
+1 to routing background and large subagent outputs to a dedicated panel, or at minimum a collapsed/click-to-expand chip as suggested in #11950.
Environment: macOS, Claude Code VS Code extension (latest).
Root cause, and a two-line fix
Replacing my earlier comment, which had the wrong mechanism (now retracted above). I traced the shipped bundle on v2.1.220 and the defect turns out to be very small — two lines, in a file that already contains the correct pattern a few lines above.
The defect
Every frame the CLI sends to the webview carries
parent_tool_use_id:nullfor main-loop frames, set for subagent frames. Confirmed on the wire.The wire→UI converter keeps that tag for
userframes and discards it forassistantframes:The render dispatcher then guards on the tag — but only on the user branch:
Subagent user frames (tool results) are already suppressed correctly. Subagent assistant frames — thinking, tool calls, response text — leak into the main chat, because the tag that would catch them was deleted one function earlier.
That asymmetry is the whole bug. The intended behaviour already exists; it simply can't fire.
Why a guard-only fix appears to work, then fails
Streamed frames take a different path — the stream assembler, which does propagate
parent_tool_use_id. So adding only the missing dispatcher guard suppresses subagent output while it streams; then the completed message is re-converted through the broken converter, the tag is stripped, and it appears. I measured this: guard-only leaves the flood exactly as it was.This also explains @thatChadM's observation in this thread — "a very brief flash of that response text that immediately disappeared and was replaced". That flash is the correctly-tagged streaming frame being replaced by the untagged complete one. Same for "flooding occurs only after the first tool call": the first tool call is what forces the streamed message to complete and be re-converted.
The fix
e.parent_tool_use_idinstead ofe.type === "user" ? e.parent_tool_use_id : null.if (t.isEmpty || t.parentToolUseId) return null;No new concepts, no content inspection, no UI work — it makes the assistant branch behave the way the user branch already does. The same ternary also drops
isSyntheticfor assistant frames, which is likely worth repairing in the same edit.Verified end to end
Applied as a local patch to v2.1.220 and measured every combination, identical subagent workload each time:
| Patch state | Live: subagent tool calls + text | After reload: notification block |
|---|---|---|
| unpatched | rendered (the flood) | present |
| guard only | rendered | present |
| both lines | gone | present |
No regressions observed. The
Agent: <name>header still renders — it's a main-loop frame and is untouched — and the collapsed<task-notification>block still appears after a reload. Only the subagent frames go.⚠️ Testing note: live and reloaded views render different things for the same event. Live shows the subagent's frames; reload shows the notification block instead. Hold that constant when testing, or you will attribute a rendering difference to your change. I reached three wrong conclusions before I did.
Regression window
@vskrsl reports v2.1.145 is clean, and the original report here is v2.1.159. That's a tight bisect range, and probably the most actionable thing in this thread.
Impact
Every subagent spawn in the VS Code extension is affected. A parallel fan-out makes the panel unusable: output from several agents interleaves with the conversation, and there is no visual distinction between an intermediate agent report and the assistant's final answer. Patterns built on subagents — judge panels, map-reduce, review fan-outs — are effectively unusable in the extension while this stands, which is why several people here have fallen back to the terminal or to
Workflow.Two lines against that seems an unusually lopsided cost/benefit.
Workarounds meanwhile
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1— removesrun_in_backgroundfrom the Agent tool schema entirely; results return as collapsed tool results.run_in_background: falseper spawn. Note synchronous is not serial — multiple Agent calls in one assistant message run concurrently, so parallelism is preserved.Workflow— N agents produce one notification rather than N (thanks @iteplov).One thing that doesn't fit
@junpicogram reports foreground subagents' response text reaching the main chat. In my testing on 2.1.220, every synchronous agent was silent — including one returning 22 KB. That may be a version difference, or something specific to three foreground agents fanned out at once. Worth re-testing on current: if it still reproduces, there is a second path I haven't found.
Still reproduces in 2.1.227 and 2.1.228.
Since my earlier comment the webview has grown most of the machinery this fix
needs — it just isn't wired into the render path. Identifiers below are minified
and build-specific; the shapes are what to search for.
What changed since ~2.1.220
The wire→UI converter used to derive one tag, and dropped it for assistant frames:
It now derives a second one unconditionally, for both frame types, stored on the
message as
sdkParentToolUseId:There is now also a helper meaning "came from a subagent", ORing both fields, plus
span tracking used by the routine that builds the render list:
What's still missing is one guard
The render dispatcher consults the tag only in the user branch. The assistant
branch filters hidden
tool_useblocks and nothing else:Widening the first early-return covers every message type at once:
Both fields matter — guarding on one fixes only half the cases
new Ap("assistant",[],{...,parentToolUseId:o})and never receivesdkParentToolUseId.sdkParentToolUseId.So keying only on
sdkParentToolUseIdleaves live output leaking, and keying onlyon
parentToolUseIdleaves reloaded history leaking.Evidence
I've been running the equivalent as a local binary patch since late July, across
2.1.220 → 2.1.228. Inline subagent output disappears from both live rendering and
reloaded history, with no other visible change to the chat. Suppression is all it
does — it doesn't give agent output its own panel the way the TUI does, which
would be the better end state.
Still open from earlier in the thread: @junpicogram's report that foreground
subagents leak too. I couldn't reproduce it — every synchronous agent was silent
in my measurements, including one with 22 KB of output. If it still reproduces,
there's a second path neither of us has found.