feat(desktop): open a subagent's session in a tab of the side panel's own

Merged
DA
8d ago
+573-118

Summary

Dean: "make subagents openable as separate tabs as well (I think this applies to Work tab...)".

A planning worker's (subagent's) session already opened in the Work tab's place from its call (#1901). It now also opens in a tab of the side panel's own:

  • Open in tab: a worker's line in the Work tab carries an icon button at its end, shown while the pointer or focus is on the line (a tooltip names it). The line's own press still opens the session in place, as before. There is no context menu on a row, so none was added.
  • The tab: named by the subagent's job (its subject, or the call's verb where none was said), wearing the worker's glyph, lit while it runs; placed after the fixed tabs and the agent tabs; closable with the × and Delete; in a compact strip it stands as its glyph alone like every unchosen tab. Opening the same session again chooses the tab already open.
  • What it draws: as an agent's tab draws its transcript (no sticky header, per #1944): the task as a folded card at the top, scrolling with the rest; the session's rows through the shared transcript blocks (planning/transcript-blocks.tsx over planning/turn-rows.ts); a shimmering "Working…" line at the end while the subagent runs, a quiet "Stopped" where it was stopped, and nothing on completion, as the agent tab's end line does. The worker is read from the plan's work on every frame, so the tab follows the session while the subagent runs.
  • The model (use-side-panel.ts): a third kind of tab, { subagent: callId }, held in the hook's state beside the fixed tabs but never written to storage; a subagent's tab chosen is kept as no tab, so a launch opens on the first fixed tab. It is held to the open plan's work as an agent's tab is to the plan's agents: on another plan it reads as the first fixed tab, and shows again when the plan with that call is back. Closing the chosen one chooses the subagent tab after it, else the plan's last agent, else the last fixed tab. The strip's drag and keyboard moves stay the fixed tabs' own, so a subagent tab stands where the agent tabs do; arrow keys reach it like any tab.
  • Coding agents: not touched. The door is PlanWork's optional onOpenInTab and the SubagentTab view over a WorkWorkerBlock, so a coding agent's subagent call could take the same path later.

Coordinated with #1944: this touches neither agent-tab.tsx nor the composer, and is rebased on main with it merged.

Tests

  • use-side-panel.test.ts: a subagent's tab opens from its call and is chosen; opened again it is chosen rather than doubled; closing it chooses its neighbour, then the plan's last agent; another plan's work reads as the board and the call back shows the tab again; a launch after a subagent's tab was chosen opens none.
  • desktop-plans.test.ts: on the real page, a worker's line offers Open in tab, which adds the tab after the fixed tabs and chooses it with the worker's glyph; the tab draws the task card, the session's call as a row that opens onto its output, its words, and the working line; Open in tab again chooses the one open; its × closes it and chooses Work.

./scripts/check.sh exits 0.

Verification

CI could not verify this UI change. CI builds nothing for the Mac, and Dean waived verify.sh for this work. The screenshots are a headless-Chrome render of the real plans page and side panel over the planning-work fixture against the built stylesheet.

The Work tab, the worker's line hovered, its Open in tab at the end:

work tab, hovered, dark

The subagent's tab opened, after the fixed tabs in the compact strip: the task card, the session's rows, and the working line:

subagent tab, dark

Light:

work tab, hovered, light

subagent tab, light

🤖 Generated with Claude Code