feat(mobile): show live sub-agents in the agent list and chat - #779
Merged
Merged
Conversation
Claude keeps emitting background-task system events after the main
turn's result lands, so an agent could read as idle on the phone while
its sub-agents were still working — or had failed. The phone now folds
those events (through the shared reducer PR 1 added) into its own store
and draws them in three places.
- store: backgroundTasks[agentId][taskId], fed from agent:event via
foldTaskEvent before the chat reducer sees the frame. Cleared on
agent:status stopped/error (idle is not a clear), on deleteChat, on
unpair, and on every handshake — missed frames can never be replayed,
so a stale "running" would otherwise never end. Never persisted.
- lib/subagents: pure derivations (top-level sub-agents only, recent
failures kept ~10 min, "quiet Nm" hint past 3 min) shared by the row,
the header line and the strip. Covered by tests/subagents.test.ts.
- AgentRow: reads as working while sub-agents run under an idle agent
("2 sub-agents" in the live slot, running dot, "Working" label); a
recent failure shows a "sub-agent failed" marker.
- Agent screen: a strip pinned above the transcript lists running and
recently failed sub-agents with tool count, elapsed and the quiet hint;
tapping one scrolls to the launching tool row (data-tool-use-id). The
header subtitle says "N sub-agents working" for an idle agent.
- Transcript (absorbs ToolRow and the block rendering ChatTab held): a
tool row keeps its running dots while its task runs, turns red with the
failure status when it fails, and expands to the sub-agent's threaded
turns — the same renderer, recursively.
- mock host: a prompt mentioning sub-agents / delegate plays a turn that
backgrounds an Agent, ends, then keeps forwarding the sub-agent's tagged
turns, progress and notification; runTurn ends the turn at the last
persisted step and plays the rest as the background tail.
…y policy The threaded `children` under an Agent tool row were rendered raw, so the sub-agent's own `result` drew a "Turn complete" divider inside the row and any policy-hidden notice leaked through. The Transcript now carries the provider's DisplayPolicy and applies it to nested logs on the way in — the same pass the chat tab makes over the main log and the desktop's SubagentThread makes over its children.
A failed sub-agent is shown for RECENT_FAILURE_MS, but the strip only ticked while a task was running and the agent row's chip had no tick at all, so once the last task failed both stayed on screen until something unrelated re-rendered them. One clock (useSubagentClock) now drives both surfaces: every second while a sub-agent runs or the row has its own turn timer, once a minute while only a recent failure is left, and no timer when there is nothing to show. The decision is the pure subagentTickMs; the rendered expiry of both the strip and the row is covered under fake timers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Mobile half of the sub-agent visibility series, stacked on #776. The phone receives the same
agent:eventstream as the desktop, so it folds Claude'ssystemtask events through the shared reducer from #776 (imported via@desktop/adapters/shared/backgroundTasks) into its own store. No sharedsrc/files touched.What the user sees
N tools · elapsed, a warn-tonedquiet Nmafter 3 silent minutes (heuristic wording, never "stalled"), and the failure status for failed ones. Tapping scrolls to the launching tool row.Store lifecycle
backgroundTasks[agentId]is folded before the chat reducer, cleared on agentstopped/error(notidle, since tasks outlive the turn), on chat delete and unpair, and reset on every handshake because task frames missed while the socket was down can never be replayed. Never persisted.Mock host
Any Claude prompt matching
sub-agent,delegate, orin parallelplays a turn that backgrounds an Agent, ends the turn, then keeps forwarding tagged sub-agent turns, progress, notification, and task-list events, so the UI can be previewed without a host.Changes
New:
mobile/src/store/backgroundTasks.ts,mobile/src/lib/subagents.ts,mobile/src/screens/Agent/Transcript.tsx(absorbs the oldToolRow.tsxso rows can render nested turns),mobile/src/screens/Agent/SubagentStrip.tsx,mobile/tests/subagents.test.ts.Modified: store index/events/chats,
AgentRow,ChatTab, agent screen,lib/tools.ts,styles/agent.css, mock script and host.Tests
Inside
mobile/:bun run check,bun run lint,bun run test(159 tests) pass.