feat(acp-chat): sidebar parity, background work menu and subagents for ACP chat
Summary
ACP chat agents (acp-chat flag) now behave like terminal agents everywhere outside the pane, and the background work they start is visible and stoppable in one place.
- Sidebar and workspace parity: status dots, chime, agent count, ports, workspace auto-naming, last-active ranking and page-comment delivery work for a pane on the chat surface.
- Standardised background work: dev servers and subagents are reported through the JetBrains AIR ACP extension that claude-agent-acp and codex-acp both speak, as one
backgroundTaskslist on the chat session. - Workspace activity menu: the Pages menu moves to the tab bar and groups background processes (with stop), subagents and pages. It replaces the background terminals button.
Also includes: a queued-prompt badge on sidebar rows, the chat session title as the pane title, scroll-to-load for older messages, tool runs that collapse once the turn moves on, and composer buttons sized to match.
Part 1: Sidebar and workspace parity
Why
Switching an agent pane to the chat surface kills its pty, which ends the terminal agent binding. Every sidebar signal is keyed by that binding, so a chat showed no status, count, ports or title, and a workspace opened straight into a chat stayed "New workspace".
How
- A
LiveSessionObserverin chat-runtime lets the host follow sessions.chat-v3/chatAgentBridge.tsreports each chat under the pane's terminal id, which the renderer now passes as an optionalterminalIdoncreateSession. - Chat bindings live in memory in
TerminalAgentStoreand merge intolistByWorkspace. The lifecycle fan-out (broadcast, activity, naming, linked-task start) is shared with the hook handler viafan-out-agent-lifecycle.ts. - The adapter pid is registered with the port scanner, and
SUPERSET_TERMINAL_IDis set in the agent env, so detached dev servers are attributed. The reaper leaves chat-owned port sessions alone. - Page watches deliver to the chat as a prompt (
promptChatSession). That only happens when a chat is attached to the terminal, which only happens behind the flag. Otherwise delivery is unchanged. workspaces.createandcreateSessiontake an optionalnamingAgent, so chat-opened workspaces get an AI title.
Part 2: Background tasks and subagents
Why
Users could not see that an agent had a dev server running or a subagent working, and could not stop either.
How
- The adapter advertises AIR
asyncTasksandnativeSubagentSessions. It foldsasync_task_*,subagent_spawned/state_updateand ACP v2subagent_updateinto a sharedBackgroundTaskstracker. - Child-session output stays out of the parent transcript. A subagent gets one transcript row, and its latest step becomes its detail, coalesced to one update a second.
stopBackgroundTaskruns protocol → router → live session → adapter (_session/async_task/stop).
| Decision | Choice | Rationale |
|---|---|---|
| Source of truth | AIR extension, not _meta.claudeCode | Both shipped ACP adapters implement it, so any AIR-speaking agent works without UI changes |
| Surface | Workspace-level menu, not a strip on the composer | Covers every agent in the workspace and keeps the composer clean |
| Stop all | Agent tasks only | Detached terminals keep a per-row stop; one click should not kill a user's dev servers |
Part 3: Desktop
WorkspaceActivityMenu(renamed fromWorkspacePagesMenu): groups with a header action, and a running-count dot on the trigger.BackgroundTerminalsButtonis removed. Its helpers moved to$workspaceId/utils/backgroundTerminals.DashboardSidebarWorkspaceQueuedBadge: queued prompt count per workspace.useLoadOlderOnReachreplaces the "Load earlier messages" button.transcriptRows: a tool run collapses once later entries exist and none of its tools are running.
Manual QA
Checked in the dev app over CDP with the acp-chat and chat-v3 flag overrides:
- Chat-opened workspace gets an AI title in the sidebar ("Add port conflict troubleshooting to README")
- Activity menu lists a chat's background dev server with stop and stop-all; Pages group has "+", no "View all"
- Sidebar row shows the working state and the port chip for a chat agent
- Header button height matches Changes/Commit (28px); composer Send matches the pickers (26px)
- Live probe against claude-agent-acp 0.85: a background shell task and a native subagent appear, show their step, and clear when done; subagent tool calls stay out of the parent transcript
Not verified end to end:
- Page comment delivered into a chat
- Queued badge with a busy chat
- Stopping a task from the menu
- codex-acp
Testing
bun testinpackages/chat(42),packages/chat-runtime(193), host-service chat-v3, terminal-agents, page-watch, notifications and terminal-agents router (264), and the desktop v2-workspace and DashboardSidebar suites (631). All pass.tsc --noEmitis clean for every touched file. The remaining errors in this worktree are ungenerated route types and auth typings on main.bun run check:i18npasses, with all new strings translated in 16 locales.- The full host-service suite has 41 failures in git-watcher, tunnel dial and git-helpers tests that this branch does not touch (
jest.advanceTimersByTimeis missing under the local Bun 1.3.2).
What changes for users without the flag
The activity menu ships to everyone, because it replaces the Pages menu and the background terminals button:
- The trigger is always in the tab bar. It shows a running dot and count for agent background tasks and subagents (terminal agents' subagents included); idle detached terminals are listed but not counted.
- Detached terminals keep a per-row stop and reopen on click. The attach debounce and a loading state carry over from the old button, and terminals shown in the right pane area (#8194) count as attached.
- Pages moved here from the right sidebar and right pane area headers.
- Terminal-agent subagents now appear in the Subagents group; clicking one opens its transcript.
Compatibility
All tRPC changes are additive and optional (terminalId, namingAgent, stopBackgroundTask, binding fields chatSessionId, backgroundTasks, queuedPrompts). Older hosts strip the unknown inputs and never emit the new fields, so the new UI stays empty.
Known Limitations
- Only AIR-speaking ACP adapters report background work. The native (non-ACP) Claude and Codex harnesses don't feed the tracker yet.
- Chat subagents are not in the sidebar agents chip, because its rows open a transcript file that only terminal agents have.
- A page watch registered while the pane was a terminal is dropped when the pane switches to chat. Publishing again from the chat re-registers it.
- Chat bindings are in memory; after a host restart they return when the pane re-creates its session.
Follow-ups
agents run --continueon a chat pane should prompt the chat instead of launching a terminal agent.
Rollout / Rollback
Behind the existing acp-chat flag. With the flag off, no chat sessions exist and every terminal path is unchanged. The exception is the activity menu replacing the background terminals button, which ships to everyone. Rollback is a revert of the three commits.
Summary by cubic
ACP chat agents behind the acp-chat flag now behave like terminal agents everywhere outside the pane, and the background work they start — dev servers and subagents — is visible and stoppable from a new workspace activity menu.
Background work
- Chat sessions report under the pane's terminal id, so sidebar status, ports, agent count, workspace naming, and page-comment delivery work as with terminal agents.
- Dev servers and subagents come from the JetBrains AIR extension that claude-agent-acp and codex-acp both speak, folded into one
backgroundTaskslist; subagent output stays out of the parent transcript, and stale or duplicate updates don't revive a finished subagent. Once the parent session is known, updates from any other session — including those arriving while a resume replays history — are treated as child activity, so a stray titled tool call never reaches the parent transcript. - Step changes travel as live-only
backgrounddeltas, so they don't journal or bump the session'supdatedAt; the task list itself stays durable. stopBackgroundTaskstops a chat's background task from the menu — only tasks the adapter knows and can stop — and detached terminals keep their per-row stop. A disconnected subagent shows as failed, and clearing a dead chat's statuses leaves it failed so it isn't treated as alive.- Chat bindings live in memory and reappear when the pane re-creates its session after a host restart; closing a fork only falls back to the original chat when the replacement never started, and a pane that adopts a branched copy closes the chat it branched from.
UI
- Pages and background work move into the tab bar's workspace activity menu, which replaces the background terminals button and ships to everyone; the menu debounces pane attachment, shows a loading state for terminal sessions, its running dot counts agent tasks and subagents rather than idle detached terminals, its clock follows every listed row, and arrow keys move between items.
- Sidebar rows show a queued-prompt badge (announced to screen readers); chat panes take the session title; resumed chats open at their end only after the session starts; older messages load on scroll once the user has scrolled up, with a retry on failure; tool runs collapse once the turn moves past them; composer buttons match the pickers.
Summary by CodeRabbit
-
New Features
- Added a workspace activity menu that brings background processes, subagents, workspace pages, and pinned pages together. Open or stop eligible background work, stop all stoppable agent tasks, and create pages from the menu.
- Workspace items display queued-message counts.
- Chat transcripts load older messages as you scroll, with progress and retry feedback.
- Supported background tasks can be stopped, and chat sessions can display updated titles.
- Workspace naming can use the selected agent when creating a chat workspace.
-
Bug Fixes
- Improved tool-call collapsing while keeping active work and pending approvals visible.
- Chat transcripts show a loading state while a session is starting, even when messages are already present.