cmux NEXT: a chat's subagents open beside it; Tile Subagents stacks them (Lawrence's zellij ask)

TE
9d ago
+1984-189

Summary

Lawrence's zellij-style ask: watching a chat's subagents work side by side, without them taking over the window. Built on #20553 (open beside), so it lands after it.

  • A subagent row opens beside the chat. Clicking a row in the chat's Subagents card opens that subagent as a tab in the pane right of the chat, by the same rule as a link (#20553). The first click makes the pane, and a lone chat docks left so the card stays in view. Later clicks add tabs to that pane. Clicking a row whose tab is already open selects that tab. Each tab is named for the subagent's task, and keyboard focus stays in the chat. Before this, each click split the chat's pane again.
  • Tile Subagents (palette agentChat.tileSubagents, plus a "Tile" button on the Subagents card) opens every running subagent of the chat in its own pane, stacked right of the chat. The stack's panes share its height equally, and again when one closes. Only that stack is equalized, not the whole screen. An ended subagent gets no pane.
  • default call: auto-tile OFF (Lawrence's zellij ask). The card's switch, renamed "Tile subagents automatically", now defaults to off. When it's on, new running subagents tile on their own as before. The coordinator agreed: it matches the decided "open beside, one right pane" model.
  • The switch is also a Settings row. Settings > General > Agent Chat has "Tile Subagents Automatically" (agentPane.tileSubagentsAutomatically in cmux.json, off by default). The card's switch and the row are the same setting. The host pushes it to chat pages with the composer's flags, and the switch writes it through pane.tileSubagentsAutomatically, the same host write as pane.showDictation. The switch's old localStorage copy is dropped, so a saved "on" from before reads as off.

SubagentBeside.swift holds the placement. AgentSubagentPanes keeps which tab shows which subagent, and the tiled stack's order.

Limits:

  • A pane's title is the task only. acpmux doesn't carry a subagent's model yet.
  • A finished subagent's pane stays open, showing its ended state.

Testing

scripts/cmux-next/tile-subagents-e2e.py drives a real chat on a fleet capture slot. It uses acpmux's fake harness, plus the subagents seed fixture: three running subagents and one done.

buildresult
red: #20553's head plus only the fixture and the script (9610be36, Mac DEV)FAIL 0/6: each row click splits again, no Tile action
dae377f3, before the dock fix5/6: the first click opened a full-width column and scrolled the chat off-screen
f627dbf3 (dock fix), Mac DEVPASS 6/6: tab beside the chat; second tab same pane; re-click selects; Tile makes 3 panes named for their tasks, none for the ended one; heights 297/297/296; after a close 446/445
  • Release compile on e26f4bbb (the feature commits): BUILD SUCCEEDED. Since then: base merges, the dock fix (SubagentBeside.swift) and test-only changes.
  • Pre-push on each Swift push: crash ratchet, concurrency, god files (nothing grew), system-blue, conventions lint. Also run on the web side: webviews bun run check, ui-rules and gen-strings --check.

The fixture's subagents aren't real acpmux subagents, so in the recording their tabs show the chat's transcript, not a subagent's own view.

Settings row: TileSubagentsSettingTests (Swift) and webviews/test/tile-subagents-setting.test.tsx, red first (no row; the switch kept its own copy), then green.

Changelog

Added: Clicking a subagent in a chat's Subagents card opens it as a tab beside the chat, and Tile Subagents stacks every running subagent in its own pane, with a Settings row to tile them automatically (off by default)

🤖 Generated with Claude Code