cmux NEXT: running subagents and background work sit in one tray above the composer; a finished subagent leaves one line

Merged
TE
15d ago
+1978-302

Leo: "put the subagent situation at the bottom of the chat and anchor". A running subagent drew a "1 subagent running" card (with an orange Claude asterisk) in the middle of the transcript where it was launched, a separate "1 agent" chip sat by the composer, and the summary popover listed it in a third style. After #23044, background work also had a "Running in background" row at the transcript's end and counts on the running line.

Now running background work shows in one place: a tray in the bottom stack, right above the composer, so it never scrolls with history. It lists running subagents, background commands, and chats the agent started:

  • Each line has:
    • an icon in the theme's accent (never a brand color),
    • the name,
    • what it does now (or its kind),
    • the elapsed time (ticking),
    • its state: Running, or Waiting when it asks you something.
  • A click opens it:
    • a subagent: its pane or view,
    • a command: its terminal (the summary card where no terminal opens),
    • a started chat: that chat.
  • A running subagent keeps its stop button, and so does a command acpmux's process registry lists. A nested subagent sits under the one that spawned it.
  • One item is its line alone. Several get a header, a time since the first started and a chevron. The header reads "2 subagents running · 1 command" during a turn and "Running in background · 1 agent · 2 commands · 1 session" between turns. Up to three lines show; more start folded so the tray never takes the transcript's room, and a long list scrolls in place.
  • Commands come from summary/backgroundWork.ts: the process registry merged with the transcript's tasks by tool call id. Started chats come from the same child-session list the summary card uses. A registry command shows whenever the registry says it runs, between turns too. The registry is read only while the summary card shows, a turn runs or a command is known to run, as in #23044, because the host's pane policy doesn't allow _acpmux/process/list. A command only the transcript knows shows only while a turn runs. That fixes the inherited bug where a peer's chat (whose registry acpmux can't list) kept "Running in background" up between turns after a task's end never arrived.
  • The glass is the user bubble's: the theme's text at a low strength over the backdrop, with the composer's hairline rim. No literal colors.
  • The transcript keeps no running card and no "Running in background" row; BackgroundWorkRow.tsx is deleted. A subagent that ends leaves one quiet line where it ended, drawn like a background task's end: "Subagent finished: · 1m 20s". The other ends read "failed" (in the danger color), "stopped" and "disconnected". A click opens the subagent's view.
  • The "1 agent" chip is gone: the tray is it. With the tray on, the running line's "· 2 agents · 1 command" counts are gone too. The tray's line component (AgentLine) is also the summary card's Subagents row, so the tray and the popover look the same.
  • Between turns a foreground subagent that a cancel or reconnect left "running" pins nothing; a background one stays.

When Debug Settings agentPane.debug.subagentTray (default on, sent with the theme as subagentTray) is off, the old presentation comes back: the group card, the chip and the running line's counts. The old subagents gallery entry and the legacy browser tests pin it off.

Coordination: #23044 (chat-background) handed this PR the "Running in background" row and the running-line counts. Its runInBackground.ts, BackgroundHint, _acpmux/process/background and directClient.backgroundWork() are left as they are.

Changelog

  • Changed: Running subagents, background commands and started chats show in one tray above the composer instead of in the chat, and a finished subagent leaves one line where it ended.

Testing

  • webviews/test/subagent-tray.test.tsx (JSDOM, real VirtualTranscript and components) covers:
    • A running subagent draws no transcript row, and the tray lists it.
    • Ended subagents get one line each at their end point. Rows and cached lines keep their identity.
    • The line text has the name and duration. A failure is data-state="failed". A click opens the subagent.
    • Tray ordering: nested agents, then commands, then started chats. Between turns only background work shows.
    • A command only the transcript knows, whose end never arrived, is in the tray during a turn and not between turns. A registry command is in the tray between turns; a registry command that exited is not.
    • Between turns the header says "Running in background · 1 agent · 1 command · 1 session". Each line opens its subagent, command or chat, and a registry command's Stop stops its process.
    • The transcript draws no "Running in background" row.
    • A single item shows its line with name, detail, time and state. Several get a header, which folds past three.
    • A waiting subagent says so.
    • The summary list renders the same AgentLine.
  • summary-background-work.test.tsx drops the tests for the deleted row.
  • agent-pane-working-counts.test.ts (browser) has a new test: with the tray on, the line counts nothing and the tray lists Reviewer, Tester and npm run dev. The file's other tests push subagentTray: false and keep testing the counts.
  • agent-pane-agents-pill.test.ts (browser) tests the tray in default mode. Its legacy tests and agent-pane-subagent-groups-open.test.ts pin the tray off. agent-pane-subagent-of-untitled.test.ts opens the subagent from its end line.
  • Gallery agent-pane.subagent-tray variants:
    • one-running,
    • several-running,
    • command-and-subagent (new): a background command plus a subagent,
    • between-turns (new): a background subagent and two started chats after the turn,
    • many-folded, finished-line, failed-line.
  • The agent-pane.background-work-row gallery entry is removed with its row.

Observed / Not run

  • On 003cd222e17, with base as of 14ff3ad55a5 (after merging feat-cmux-next again, with #23090, #22819 and #23093):
    • scripts/web-lint.sh: format clean, 0 type or lint errors. It exits 1 on three warnings in files this PR doesn't touch (HistoryPage.tsx, IncomingMessageCard.tsx, sidebar-rail-css.test.tsx).
    • check-switch-fetches.py passes, and so does its --self-test.
    • prepush-gate.py: no new violations.
  • Changed after the review of 736afd7935c:
    • useNow, the shared one-second clock behind the tray, the group card and the summary rows, rests while the page is hidden and catches up when it shows. Its visibilitychange listener is allowlisted in check-switch-fetches.py because it reads no data.
    • In a short pane the tray gives up its height and scrolls in place, like the other parts of the bottom stack.
    • New JSDOM test: the tray's time does not move while the page is hidden and updates when it shows. It records the clock's interval and sets the time by hand, with no real wait.
  • On 445b6b12786 (before this change), focused bun test passed: subagent-tray.test.tsx 9/9 and 83 related tests.
  • CI on 14ff3ad55a5 had two reds of this PR's own, both fixed in 003cd222e17:
    • agent-pane-agents-pill.test.ts:273 counted 3 line times for 2 lines. The header's time used the lines' class; it now has its own (acpmux-running-tray__time).
    • agent-pane-session-pages.test.ts "passes the host's pane policy": reading the registry whenever the tray was on sent _acpmux/process/list, which the policy refuses. That read is reverted.
  • Not run on 003cd222e17, so CI is the first run of each:
    • Focused bun test was blocked on this Mac by the local-load hook. The fleet refuses the focused-file variable (CMUX_WEB_TEST_FILES).
    • The browser tests.
    • The gallery plays.
    • The Swift build. The Swift change is one Tunable<Bool>, its descriptor in AgentPaneTunables.all, and one key in AgentPaneTheme.values, all copied from existing tunables.
  • The strings are unchanged by this push: the header reuses bgWork.running and the existing turn.working.* plurals. The four subagents.ended.* strings from the first push are in all 21 languages.

No capture: the native files only add AgentPaneTheme and SubagentTray tunables the webview reads; the tray's states are gallery entries, which the gallery advisory run captures.

Agent session: cosmic-gray

🤖 Generated with Claude Code