fix(agent-pane): keep a background helper's step off the finished turn
Leo, looking at a finished agent chat: "why does it say you're still running grep". Asked whether background work should be kept apart from the turn above it: "yes let's do that".
What he saw
The turn had ended. Its folded "Worked for 2m 19s" line came first, then the final reply with its actions and "19 minutes ago". Under all of it was a live row, "Running grep -nE … /tmp/ux-gallery-bg/ci.log | head -40", with a chevron. The grep belonged to a background helper the agent had started (Agent with run_in_background), but the row sat under the finished turn's footer as if it were that turn's work. It also never ended, because the grep's completion went to the helper and never reached that row.
Cause
I read the session's acpmux event log. A Claude subagent's lines arrive under their own session id, <chat>/<Agent call>, and acpmux tags them with that same id (_meta.acpmux.subagent). Just after host_adopted, the hub had lost its subagent tree, and it only rebuilt it at the helper's next progress event. The two lines in that gap arrived untagged: the previous call's end, and the tool_call for the grep. The pane treated the grep as the chat's own call. It landed after the turn's summary (turns.ts: "anything after the summary … draws as it came"). The grep's end arrived tagged again, so it went to the helper, and the transcript row kept saying "Running" forever.
Fix
SubagentFold.reduce now looks at the update's own sessionId when the tag is missing. The update is treated as the helper's if that id is a subagent the pane already knows, or if it is the chat's session plus a subagent part (<session>/<…>). The second check covers a reopened chat whose page starts after the spawn. Such an update now goes where the helper's tagged work already goes:
- The running tray above the composer. It stays the one separate place for background work, and lists the helper by name. Between turns its header reads "Running in background" once there is more than one line. The helper's live step is in the line's tooltip.
- The helper's own view. Opening the line shows the grep running, and then done.
The chat's own late tool updates after a turn are unchanged: only an update whose session is a subagent's moves.
Design: this reuses the existing tray (design/running-tray.md, design/chat-transcript.md "Live activity rows": "The running tray above the composer is the only running-activity surface"). Nothing new is drawn, so I made no gallery variants and changed no spec. The line's anatomy stays as settled in cmux #29276 (name only, live step in the tooltip, no subtitle). No component removed, no default flipped.
The cause on the acpmux side (the hub's subagent tree dropping helpers on host_adopted until their next progress event) is a separate follow-up. This PR makes the pane right whatever the hub tags.
Tests
bun test test/agent-pane-background-subagent-after-turn.test.ts replays the case through AcpmuxDirectClient: a finished turn with a background helper, then the helper's untagged grep under <chat>/<call>.
- On base (8d700a6197d, the test commit before the fix): 2 fail. The grep is a row of the chat's transcript after the turn's footer, both with and without the spawn in the page.
- On head: 3 pass. The step is in no transcript row and draws nothing after the footer. It runs in the helper's view,
trayItemslists the helper by name with the grep as its action, and its tagged end completes it there. A control case checks that the chat's own late call still draws as before.
Observed / Not run
- Ran: the focused test (red/green above). Ran the subagent, tray and background suites (
src/agent-session/acpmux/subagents/,subagent-tray,agent-pane-background-*,agent-pane-ctrl-b-background,agent-pane-agents-pill,session-info-subagent,subagent-stopped-after-cancel): 215 pass, 0 fail. Also ranbun run strings,check-default-cursor.py(ok) andcheck:popup-hover-reveals(ok).CMUX_LOCAL_HEAVY_CHECKS=0 bun run checkpasses exceptvp fmt --checkontest/agent-pane-older-rows-fade.test.ts, which fails the same way on base and is not touched here. - Not run: a DEV build that replays a host adoption mid-subagent.
Changelog
Fixed: A background subagent's step no longer shows as a "Running …" row under a finished turn. It shows on the subagent's line in the running tray and in its own view.
risk:normal
Agent session: agent session
🤖 Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes the agent pane showing a background subagent's step as a "Running …" row under a finished turn, where it stayed "Running" forever because its end went to the helper.
SubagentFold now attributes an untagged update by its own sessionId when acpmux misses the subagent tag (a hub that just adopted its host lost its subagent tree until the helper's next progress event). The step shows in the helper's own view and on its line in the running tray; the chat's own late calls still draw as before.