cmux NEXT: a subagent's pane is named after the subagent, not its parent chat

Merged
TE
6d ago
+270-12

Leo, 2026-10-30:

"subagents shouldn't be tab-named the exact same as the parent. and we dont want that subtitle text"

And, when asked which subtitle he meant (20:05Z):

"i mean the sidebar tab shouldn't just be named claude sr2 coord if you can just say it's SUbagent of sr2 coord / and yes ofc remove the second line a)"

This PR is the tab names: a subagent's pane is named after the subagent, else "Subagent of ", never the chat's bare name. The second line is #28510.

What was wrong

A subagent's pane is an agent tab on its chat's own acpmux session (AgentSubagentPanes). The tab strip and the sidebar's tab rows name an agent tab by its session (TabTitleResolver → AgentChatTabTitle), so the subagent's tab got exactly the chat's name, e.g. two "claude-sr2-coord" tabs.

What changes

  • The page sends the subagent's name with chat.subagentPane (title): its own (Subagent.name: its task, which for the Agent tool is the call's description), else "Subagent of " from the page's already-translated subagents.of label (subagentTabTitle).
  • The name follows what the pane shows. A pane opened on a subagent shows the chat's other subagents in place, and its "Subagent of" pill goes back to the chat. The page reports each change (chat.subagentShown, only once it has that subagent's rows), and the tab takes that subagent's name, or the chat's again.
  • The app keeps it per tab (SubagentTabTitles, CmuxNextAgentPane). It's recorded by session and subagent id when the request arrives, then bound to the tab once that tab is created.
  • TabTitleResolver.untitled reads it before the chat's title. A name the person gives the tab still wins, because displayTitle is read first. The strip and the sidebar's tab rows share that rule, so both change together.
  • An older page that sends no name leaves the tab as before. The openSubagent hook keeps its signature.
  • A subagent tab restored after a relaunch shows its whole chat, so it's named like the chat again.

Nothing else is removed or restyled. The "Subagent of …" pill and the sidebar subtitle are unchanged here.

Tests

  • SubagentTabTitleTests:
    • the subagent's tab reads its own name while the chat keeps its own (before: identical);
    • the name follows what the pane shows, and clears back on the chat;
    • blank, unnamed, other-session, over-long and closed-tab cases;
    • both requests decode.
  • webviews/test/subagent-pane-tab-title.test.ts: the request carries the name; a subagent with no name of its own gets "Subagent of ". 2 pass on cmux-ci, and lint passes.

Screenshots

Capture state scripts/cmux-next/media/states/subagents-four-panes.json (four fixture subagents opened beside a chat). Both sides carry #28213's debug open_subagent action and fixture agents count, which a showcase capture needs to open a subagent pane at all:

  • before: 23db08cd511072ca368bbb397a2d302f4e67e671 = base df3ee368 + #28213 6e063290.
  • after: e2bd98ef1e9f624fa9a8d6f02e3cb52eb4e63629 = this PR 9d12f819 + #28213, plus one stills-only line. #28213's debug action calls the open hook directly and so skips chat.subagentPane; the line makes it record the fixture subagent's name ("Reviewer", "Auditor", "Helper N") the way a tray click's request does. That line goes into whichever of #28213 and this PR lands second.
LightDark
Before: every subagent's tab ("Right c…") and sidebar row ("Agent") reads like its chatbefore lightbefore dark
After: sidebar rows Reviewer, Auditor, Helper 1, Helper 2; tabs Review…, Audito…, Helper, Helperafter lightafter dark

Each subagent's sidebar row reads its own name (Reviewer, Auditor, Helper 1, Helper 2); the chat keeps its own row. The tab strip shows the same names, cut at this pane width. Nothing else in the window changes.

Viewed after image: yes
Visual verdict: SHIP: each subagent's row and tab carry its own name, as Leo would expect next to ChatGPT or Claude.

Not this PR: at this pane width the tab chips cut "Helper 1" and "Helper 2" to the same "Helper" (base cut "Right column" to "Right c" the same way). A tab-strip follow-up should keep a trailing number or truncate in the middle.

Agent session: agent session

🤖 Generated with Claude Code