Repository navigation
feat(workspace): give terminal panes titles, names, pins, headers and status like agents #865
Description
Motivation
Plain terminal panes are first-class sessions everywhere that matters structurally: they are grid leaves, Dispatch rows, command targets, operator workspace.observe entries and tmux-persistent across restarts. But most session features still ask "is this an agent?" (isAgentProviderKind, ~40 checks) where they actually mean "is this a session the user manages". The result is that a shell cannot be titled, named, pinned, jumped to by its label, auto-followed, or seen as busy, and its pane header is a hand-rolled strip (terminal $) that predates every header feature added since April: folder, color flag, title/name row, Status Mode fill.
Prompt templates and the Key Vault already reach terminals (#830 / #831 via #840). This issue finishes the job for everything else that makes sense on a shell, for both kinds of terminal:
- plain shell panes (
SessionKind'terminal',TerminalLeaf, tmux-backed), and - agent panes shown in the raw terminal view (
AgentTerminalLeaf, including the OpenCode Terminal runtime).
Several of the exclusions were deliberate at the time (#660 "plain shell terminals are intentionally out of scope", #836 "Terminals are never named", the #152 v1 deferral of pins). Those decisions are being reversed on purpose: a title, a name, a pin and a header are session metadata, not transcript features.
Intended behavior
- Titles: "Set Title…" (command id unchanged,
agent.title.set) and operatoragents.titleSetwork on any session. - Spoken names: with the Agent names setting on, terminals get a stable name from the same pool. Operator search finds them; the operator guide routes a named shell to
terminals.input, neveragents.prompt. - One header: plain terminals render the shared
PaneHeader(no forked markup, per fix(workspace): light the status header for terminal-view agent panes #853): live folder, color flag, title/name row, Status Mode fill, TAIL pill, and the foreground command as the badge. - Activity: a terminal is
runningwhile a foreground command other than the shell runs (tmuxpane_current_command, or node-pty's foreground process for direct PTYs). A command finishing marks the row NEW. The same signal feeds Status Mode, tab running counts, the Dispatch subtitle (shell running · npm), close confirmation, Close Old Agents and the Activity modal. - Navigation and organization: pin/unpin, jump by label (
A2), Auto-follow, Jump to Latest and Auto-follow All, and the Agent Status panel include terminals. - Operator:
agents.locate/show/close/restore/list/search/titleSet/pinSetaccept terminals;agents.promptkeeps refusing them. - Terminal view: an agent pane in Terminal view shows which related agent it is displaying and can return to the parent without resizing the TUI (bug(workspace): terminal view hides which related agent a pane is showing #858).
- Fixes found during research: Dispatch "New Agent…" picker offers Terminal; the Activity modal can bury a terminal; a terminal's ERROR badge shows in Dispatch; the Worktrees panel lists shells; terminal panes carry
data-pane-id; "Undo Clear Composer" is hidden on shells; OpenCode Terminal hides transcript-only commands that silently did nothing; template Replace/Append copy is honest for terminal targets.
Stays agent-only
Prompt delivery (deliverPromptToAgent, agents.prompt), transcript features (rewind, resume, Reader, Copy Last Response, duplicate, provider switch), composer features, and the remote phone client (a deliberate security boundary; its leak is tracked separately).
Acceptance criteria
- Every behavior above has a colocated test that fails without the change; tests that asserted the old exclusions are rewritten, with the reason in the diff.
- The terminal foreground poller runs only while at least one terminal session exists, never overlaps itself, and emits only on change.
-
npm run typecheck,npm test,npm run check:keybindingsandnpm run test:contractpass on Node 24. - No new renderer re-render per PTY chunk in
TerminalLeaf(primitive store subscriptions only).
Related: #851 / #853 (terminal-view header), #857 (OpenCode Terminal running signal, out of scope here), #858, #830, #831, #816, #660, #152.
Activity
chore(workspace): merge origin/main into terminal session parity
test(sessions): follow main's two contract changes into this branch's…