Orchestration pill bar updates: same-pane pills, 3-dot menu, hover card, breadcrumbs#9680
Draft
Orchestration pill bar updates: same-pane pills, 3-dot menu, hover card, breadcrumbs#9680
Conversation
V1 hid the pill bar from any child agent view (active conv has parent) and unconditionally rendered breadcrumbs in its place. The new design keeps the pill bar visible from same-pane child views (so users can switch sibling -> sibling in place) and reserves breadcrumbs for child views that have been split off into a separate pane/tab. Changes: - Drop the early-return in pill_specs when active conv is a child; resolve the orchestrator and build pills for both orchestrator and same-pane child views, marking the active conv's pill as Selected regardless of which it is. - Add is_split_off_child(controller, app) helper. Returns true iff the active conv has a parent AND the parent is the active conv in a different terminal view in this window (per ActiveAgentViewsModel). - Gate render_orchestration_breadcrumbs on is_split_off_child so same-pane child views fall through to pills. - Short-circuit pill_specs for split-off views (the breadcrumbs in the title would otherwise stack with a redundant pill row below). - Add AgentViewController::terminal_view_id() accessor so the helper can compare pane identities. Co-Authored-By: Oz <oz-agent@warp.dev>
When a child agent has been opened in a different terminal view than the orchestrator's view (via 'Open in new pane' / 'Open in new tab', or restored that way), its pill in the orchestrator's pill bar now swaps the avatar disc for a pin glyph and clicks dispatch RevealChildAgent (which focuses the existing pane) instead of SwitchAgentViewToConversation (which navigates in place). Changes: - New PillPinState enum on PillSpec; orchestrator pill never carries pin state, child pills query ActiveAgentViewsModel to detect a different active terminal view than this one. - render_pill swaps the avatar for an Icon::Pin when pinned. Pinned pill clicks dispatch TerminalAction::RevealChildAgent. - Add Icon::Pin variant + assets/bundled/svg/pin-01.svg (Lucide-style pushpin). - Extend the pane_group RevealChildAgent handler with a fallback that focuses an already-visible terminal pane whose terminal view has the conversation as its active agent-view conversation. New helper PaneGroup::find_visible_terminal_pane_for_conversation walks visible terminal panes (skipping hidden-for-close) to perform that lookup. Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Add the 4 new TerminalAction variants the design's 3-dot overflow menu will dispatch and route them through TerminalView::handle_action so the backend behavior is fully wired: - OpenChildAgentInNewPane / OpenChildAgentInNewTab: emit Event::RevealChildAgent (the pane group reveals a hidden child pane for the common case and now also focuses an already-visible pane via find_visible_terminal_pane_for_conversation, added in Phase B). Tab routing for OpenChildAgentInNewTab is a follow-up; for V2-of-V2 both paths land on the same handler. - StopAgentConversation: cancel the ambient task via cancel_task_with_toast when the conversation has a task_id; logs a TODO for local-conversation cancel. - KillAgentConversation: cancel the ambient task (if any) and remove the conversation from local history. Cloud-side deletion is intentionally skipped per V2 non-goals. The visible 3-dot button + dropdown menu UI is intentionally deferred to a follow-up phase \u2014 this commit just gets the action surface in place so the menu can dispatch through it. Co-Authored-By: Oz <oz-agent@warp.dev>
Phase B introduced pin detection by querying ActiveAgentViewsModel for each child agent and comparing the registered terminal view id against the orchestrator pane's view id. The check fired for every child because ActiveAgentViewsModel registers the *hidden* child agent terminal view (created by StartAgentExecutor via create_hidden_child_agent_conversation), so every child agent always has a different view id than the orchestrator pane. Result: every child pill rendered with the pin glyph instead of an avatar disc, and every click routed through RevealChildAgent rather than SwitchAgentViewToConversation, breaking the in-place same-pane switching that Phase A established. Disable pin detection (force PillPinState::Unpinned for all children) until pane visibility is properly plumbed into ActiveAgentViewsModel or PaneGroup. The PillPinState enum, pin glyph rendering path, and RevealChildAgent dispatch are all kept intact behind that flag so turning pin detection back on becomes a one-line change. Co-Authored-By: Oz <oz-agent@warp.dev>
Phase C wired up the four TerminalAction variants (OpenChildAgentInNewPane, OpenChildAgentInNewTab, StopAgentConversation, KillAgentConversation) but left the visible UI deferred. This commit adds the menu surface. Each child pill now renders a trailing 3-dot button (Icon::DotsHorizontal). Clicking it opens a Menu<OrchestrationPillBarAction> with four items: "Open in new pane", "Open in new tab", "Stop agent", "Kill agent". Menu items dispatch the existing Phase C TerminalActions through the PaneHeaderAction custom-action surface, which TerminalView::handle_action already handles. Implementation: * Added OrchestrationPillBarAction enum (OpenMenu, CloseMenu, plus the four menu-item variants, each carrying the target child's AIConversationId so a single Menu instance can serve every child). * Made OrchestrationPillBar a TypedActionView<Action=OrchestrationPillBarAction>; changed terminal/view.rs creation site from add_view to add_typed_action_view accordingly. * Added a single Menu<OrchestrationPillBarAction> child view + menu_open_for: Option<AIConversationId> state. Items are rebuilt per-open with the targeted child's id baked in. * Subscribed to MenuEvent::Close so click-outside / ESC dismissal flows back through CloseMenu. * Render() now wraps the bar in a Stack with the menu as a positioned overlay anchored to BottomLeft when menu_open_for.is_some(). Per-pill anchoring is a follow-up; current placement lands the menu beneath the bar. * Highlight active-menu pill the same way as is_selected so the user can see which pill the open menu is targeting. * Added separate overflow_button_mouse_states map so the 3-dot button has its own hover highlight independent of the pill body, and clean it up alongside mouse_states on RemoveConversation / EnteredAgentView / ExitedAgentView events. Auto-close the menu if the targeted child disappears. Co-Authored-By: Oz <oz-agent@warp.dev>
The previous overlay used `OffsetPositioning::offset_from_parent` with a fixed offset from the pill bar's BottomLeft, so every pill's menu opened in the same place at the far left of the bar regardless of which 3-dot button the user actually clicked. Switch to `PositioningAxis::relative_to_stack_child` anchored to a per-pill saved position id (`overflow_button_position_id(conversation_id)`). Each child pill's 3-dot button is wrapped in `SavePosition` so its painted rect is registered in the position cache; when the menu opens, View::render anchors the menu's top-right corner to the button's bottom-right corner with a 4px gap (XAxisAnchor::Right -> Right, YAxisAnchor::Bottom -> Top). Now the menu opens directly under whichever pill's 3-dot button was clicked, no matter how far across the bar that pill happens to sit. Co-Authored-By: Oz <oz-agent@warp.dev>
Phase D from the V2 plan: a per-pill hover details card that surfaces the agent's name plus its task description, branch, and PR (when any PullRequest artifact is attached to the conversation). Mechanics: * Added `OrchestrationPillBarAction::SetHoveredPill(Option<id>)` and a `hovered_pill: Option<AIConversationId>` field on the pill bar. * Each pill body's Hoverable now opts into a 300ms hover-in delay / 80ms hover-out delay and dispatches `SetHoveredPill` from its `on_hover` handler. The delay matches the standard tooltip cadence so scrubbing across the bar doesn't pop a card per pill. * Wrapped the pill body in `SavePosition` keyed by `pill_body_position_id(conversation_id)`. `View::render` uses that saved rect (via `relative_to_stack_child`) to anchor the card under the hovered pill, mirroring how the 3-dot menu anchors to its own saved rect. * Made the menu and card mutually exclusive at the overlay level: when `menu_open_for` is Some, we render the menu and ignore `hovered_pill`. `open_menu_for` also clears `hovered_pill` to be defensive. Card content (V1, hide-if-missing): * avatar disc + bold agent name * description paragraph (title or initial query, ~200-char truncation) * branch chip (Icon::GitBranch) and PR chip (Icon::Github + repo#NNNN) derived from `Artifact::PullRequest`, when present. Status badge, harness chip, working-directory line, and diff-stats from the original spec are deferred until the data they depend on lands on `AIConversation`/`AmbientAgentTask`. Co-Authored-By: Oz <oz-agent@warp.dev>
Fills out three of the previously-missing fields from the Figma:
* **Status badge** (Working / Done / Error / Cancelled / Blocked):
reads `conversation.status()` and reuses the same icon+color mapping
from `status_icon_and_color` (already used by the conversation
details panel and the agent run row), so the card and the side
panel can't drift on what 'Working' looks like.
* **Working directory line**: pulls from
`AIConversation::initial_working_directory()` with a fallback to
`current_working_directory()` for ambient agents whose root task
hasn't yet recorded a CWD. Prefixes with `~/` when the path is
rooted at `$HOME`.
* **Harness chip**: uses `ai::harness_display` (icon, label, brand
color) so 'Claude Code' renders with the orange brand color, 'Gemini
CLI' with blue, etc. Defaults to Warp Agent (Oz) when server
metadata hasn't loaded yet (in-progress local conversations) so the
chip slot doesn't pop empty.
Branch chip and PR chip continue to come from
`Artifact::PullRequest`, hidden when no PR is attached.
Still deferred (need new server fields / artifact variants):
* standalone `Branch` artifact for branches without a PR yet
* diff stats (`+N -M`) — needs an `Artifact::DiffStats` variant
or a local git read
Co-Authored-By: Oz <oz-agent@warp.dev>
Two related fixes for the hover details card.
1. **Orchestrator status is misleading**: `conversation.status()` reports
the conversation's *own* last-exchange status. For an orchestrator
that has handed off to subagents, that status often ends up as
`Cancelled` (the user cancelled to delegate) or `Success` (the
orchestrator's own streaming finished), which doesn't reflect the
state of the orchestration as a whole. Hiding the badge for
orchestrator pills (no parent conversation) is the cheapest correct
fix until we plumb a child-status aggregation accessor.
2. **Status badge overflowing the card** on the orchestrator pill only:
the header used `MainAxisAlignment::SpaceBetween` with the leading
group sized `Min` and the name's `max_width` of `HOVER_CARD_WIDTH -
110`. When the name is long enough to fill its budget, SpaceBetween
pushes the badge past the right edge of the card instead of
truncating the name. The orchestrator hits this because its title
("Orchestrate Multi-Agent Label Edits") is much longer than child
names. Fix: cap the badge with a `STATUS_BADGE_MAX_WIDTH = 96`
ConstrainedBox and compute the name's max_width as the remaining
horizontal budget so the badge always fits inside the card.
Co-Authored-By: Oz <oz-agent@warp.dev>
Wraps the bar in `Clipped::new(..)` so when the orchestrator's pane is narrower than the natural width of the pill row (orchestrator + N child pills), the pills get cut off at the pane boundary instead of painting over whatever pane sits to the right. The row uses `MainAxisSize::Min` and the parent agent-view header doesn't enforce a horizontal bound, so without the explicit clip the trailing pills (and the bar's own background fill) leak past the pane divider in split layouts. Only the bar itself is clipped; the menu / hover-card overlays stay outside the Clipped wrapper so they can still extend beyond the bar bounds when anchored to the trailing pills. Co-Authored-By: Oz <oz-agent@warp.dev>
Previous attempt wrapped the bar in `Clipped::new(..)` to keep pills inside the pane but the row was still `MainAxisSize::Min`, which reports the row's *full intrinsic width* as its laid-out size. `Clipped` clips at its child's reported size, so when the row's intrinsic width already exceeded the pane width, Clipped's bounds were the same oversized rect and nothing got cut off. Switching the row to `MainAxisSize::Max` + `MainAxisAlignment::Start` makes the row's laid-out width match the parent constraint (the pane width passed in by the wrapping Flex::column in pane_impl.rs). Children remain left-packed; any pills past the right edge of the row paint outside its bounds and get clipped by the surrounding `Clipped` element. Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
The dots used to render unconditionally, which made every child pill read as having an action affordance even at rest. Hide the glyph (and its hover background) until the pill body is hovered or its menu is already open. Slot is still reserved at the same width so neighbouring pills don't shift on hover \u2014 we just swap the icon for `Empty` when not visible. Button keeps its mouse handler and `SavePosition` either way; the menu's anchor stays correct, and clicks on the slot still fire even when the glyph is invisible. Co-Authored-By: Oz <oz-agent@warp.dev>
Pill width is now determined by avatar+label alone. The 3-dot overflow button is rendered as a positioned overlay anchored to the pill's trailing edge (Stack + add_positioned_child at MiddleRight) and only shown when the pill is being hovered or its menu is open. Result: no slot reservation, no width change between rest and hover, no shift of sibling pills, and the dots visually clip the trailing edge of the label text rather than pushing it aside. Co-Authored-By: Oz <oz-agent@warp.dev>
Two fixes for the overlay 3-dot button: - Shrink the label's max width by the overflow button's footprint when show_dots is true so the ellipsis truncates *before* the dots rather than running underneath them. At rest the label still gets the full budget so the pill keeps its compact width. - Add with_defer_events_to_children() on the outer pill body Hoverable. Previously a click on the 3-dot button fired *both* handlers (open menu *and* switch agent view); deferring lets the inner Hoverable consume the click so only the menu opens. Co-Authored-By: Oz <oz-agent@warp.dev>
…ed pill - Always use the shorter label budget (PILL_LABEL_MAX_WIDTH minus the 3-dot button footprint) for child pills regardless of hover/menu state. Switching the budget on hover caused the pill to *shrink* when dots appeared, since Min sizing propagated the smaller width outward and shifted siblings. With a fixed budget, child pill widths are constant; only the dots overlay appears/disappears. - Drop menu_is_open_for_this from the 'selected' branch of the highlight rule. Opening the 3-dot menu on a non-active pill now paints that pill with the regular hover background instead of the full selected (foreground/background-inverted) treatment, so only the truly active pill reads as selected. Co-Authored-By: Oz <oz-agent@warp.dev>
Previously, OpenChildAgentInNewTab routed through RevealChildAgent, which only reveals/focuses the existing hidden child pane within the orchestrator's pane group \u2014 so picking 'Open in new tab' just opened a vertical split, not a new tab. Wire a real new-tab path: - Add Event::OpenChildAgentInNewTab on TerminalView. - In TerminalView::handle_action, OpenChildAgentInNewTab emits this event instead of RevealChildAgent. - terminal_pane.rs forwards it as pane_group::Event::OpenChildAgentInNewTab. - Workspace handles the event by calling add_new_session_tab_with_default_mode and then invoking enter_agent_view_for_conversation on the new tab's active terminal view, switching focus to that tab. The conversation already lives in BlocklistAIHistoryModel so no restoration plumbing is needed \u2014 the new terminal view simply enters agent view for the existing conversation id. Co-Authored-By: Oz <oz-agent@warp.dev>
In a split-off pane that's been resized down, the orchestration breadcrumb row (parent avatar/title \u203a child avatar/title) easily exceeds the title slot's available width and the trailing crumb gets clipped with no way to read it. Wrap the breadcrumb row in a horizontal\n`NewScrollable` so the user can pan to reveal the clipped portion. Switch the row to `MainAxisSize::Min` so its intrinsic width is the sum of its children (rather than always filling the title slot, which would defeat the scrollable). Persist the scroll handle on `TerminalViewMouseStates` so scroll position survives renders, and plumb it into `render_orchestration_breadcrumbs`. Aliased `warpui::elements::Fill` as `ElementFill` to avoid colliding with the existing `warp_core::ui::theme::Fill` import. Co-Authored-By: Oz <oz-agent@warp.dev>
Switch ScrollableAppearance::new(.., overlaid_scrollbar=true) on the breadcrumb's horizontal scrollbar so it paints on top of the row instead of reserving a strip below it. Reserving space pushed the breadcrumbs upward (off-center) when the row overflowed; with the scrollbar overlaid the row stays vertically centered in the title slot, with the scrollbar briefly crossing through the bottom of the labels when scrolling. Co-Authored-By: Oz <oz-agent@warp.dev>
When the orchestrator is already open in another pane (same tab, different tab, or different window), clicking the parent breadcrumb now focuses that existing pane via WorkspaceAction::RestoreOrNavigateToConversation instead of switching the current pane in place. Falls back to TerminalAction::SwitchAgentViewToConversation when the orchestrator isn't open anywhere, so the breadcrumb remains useful even after the orchestrator's pane has been closed. Co-Authored-By: Oz <oz-agent@warp.dev>
Each pill's hover card now fetches `git diff --shortstat HEAD` against the conversation's working directory the first time it's hovered, caches the result on the OrchestrationPillBar, and renders a chip matching the styling of the prompt git diff stats chip (`add_color`/`remove_color` from `code::editor::diff`). Orchestration child agents typically run in their own git worktrees, so per-conversation cwd resolves to a per-agent change count. The cache is cleared whenever the orchestrator changes so stale stats don't leak across orchestrations. Co-Authored-By: Oz <oz-agent@warp.dev>
Child agents in worktrees typically commit their work as they go, so `git diff --shortstat HEAD` reports 0 immediately after each commit and the hover card chip never appears. Switch to a new `get_branch_change_summary` helper that diffs against the detected main branch (committed-since-fork + uncommitted), giving the cumulative "diff that would land in a PR" change count per agent. Falls back to HEAD-relative semantics when on the main branch directly. Co-Authored-By: Oz <oz-agent@warp.dev>
Branch-vs-main diff inflates the count when a branch was forked from an older commit (the chip showed +81/-1545 for a +4/-8 actual change). Pulling the chip out for now \u2014 will re-add with smarter base detection later (likely merge-base / fork point with cap on the lookback range). Reverts the OrchestrationPillBar diff_stats fields, async fetch, and the +N -M chip; also removes the now-unused get_branch_change_summary helper. Co-Authored-By: Oz <oz-agent@warp.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Builds out V2 of the orchestration pill bar in the agent view header — same-pane pills with avatars + click-to-switch, 3-dot overflow menu, hover details card with status / cwd / harness / PR /
+N -Mgit diff stats, and split-off-only breadcrumbs that route back to the orchestrator's existing pane/tab when it's already open.Highlights:
SwitchAgentViewToConversation.Open in new pane,Open in new tab,Stop agent,Kill agent. The menu is a single sharedMenu<OrchestrationPillBarAction>rebuilt per-open with the targeted child id.Event::OpenChildAgentInNewTabplumbed up fromTerminalView→pane_group::Event→Workspace, then enters the agent view in the fresh tab.Artifact::PullRequest, and a new+N -Mdiff stats chip styled like the prompt git diff stats chip.get_repo_git_summary(cwd)and cached on the pill bar; cache cleared on orchestrator change. Worktree-per-child setups give per-agent stats.[Parent Avatar] Title › [Child Avatar] Name, wrapped in a horizontalNewScrollablewith overlaid scrollbar so a narrow pane can pan and the row stays vertically centered.WorkspaceAction::RestoreOrNavigateToConversationwhen the orchestrator is already open in another pane/tab/window (focuses it, switching tabs/windows as needed); falls back toSwitchAgentViewToConversationin the current pane otherwise.Gated by
FeatureFlag::OrchestrationPillBar.Testing
Tested locally on macOS:
git diff --shortstatcompletes.cargo check -p warpandcargo clippy -p warp --all-targets --tests -- -D warningsboth pass.Server API dependencies
No server API changes.
Agent Mode
Changelog Entries for Stable
CHANGELOG-IMPROVEMENT: Added an orchestration pill bar to the agent view header for navigating between an orchestrator and its child agents, with a hover details card, 3-dot overflow menu, and split-off-pane breadcrumbs.
Conversation: https://staging.warp.dev/conversation/d6c90117-65f0-402d-a125-d6e1d4f0d331
Plan: https://staging.warp.dev/drive/notebook/afADGVQdVqXEGFbnDXVuYR
Co-Authored-By: Oz oz-agent@warp.dev