Delegate Popover
Hand a record or a selection to an agent — pick one of your agents, say what to do and, for a task, what happens when it finishes.
- Status
- Since
0.25.89- Accessibility pattern
- labelled fields, listbox keyboard, disabled reasons shown
Last updated
- Assignee
- Delegate
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Delegate Popover before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify delegate-popoverInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/delegate-popoverRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/button, @vegastack/empty, @vegastack/label, @vegastack/people-picker, @vegastack/popover, @vegastack/select, @vegastack/separator, @vegastack/skeleton, @vegastack/spinner, @vegastack/textarea.
Usage
import { AgentSlot } from "@/components/ui/agent-panel";
import { DelegatePopover } from "@/components/ui/delegate-popover";
<DelegatePopover
agents={agents.map((a) => ({
id: a.id,
name: a.name,
access: a.accessSummary,
disabledReason: a.readOnly ? "Can't — read only" : undefined,
}))}
whenFinishedOptions={[
{ value: "review", label: "Move to review" },
{ value: "done", label: "Mark done" },
]}
onConnect={() => router.push("/settings/agents")}
onDelegate={({ agentId, instructions, whenFinished }) =>
delegate(task.id, { agentId, instructions, whenFinished })
}
>
<AgentSlot agent={delegation?.agent} state={delegation?.state} />
</DelegatePopover>;The child is the trigger — the Delegate property's AgentSlot, a peek's AgentChip, a bulk bar's
button. The host passes the viewer's agents and receives { agentId, instructions, whenFinished };
a returned promise keeps the popover open, its button busy, until it settles, and open with the
choice if it fails. The host shows the toast ("Delegated to Claude Code · Undo"). The D key opens
it through open and onOpenChange.
Scope
One popover for tasks and table rows. Leave whenFinishedOptions out for rows and bulk, and give
bulk its own title ("Delegate 40 rows to…"). An agent that can't take the work is still listed,
disabled, with its disabledReason. With no agents it explains how to connect one. Choosing a
person is the PeoplePicker; showing an agent already on the work is the AgentPanel.
Examples
- Assignee
- Delegate
States
Bulk rows (their own title, no When finished), no agents connected, and agents loading.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
agents* | readonly DelegatePopoverAgent[] | — | The viewer's agents, in order. [] shows the connect empty state. |
children* | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | — | The trigger: an AgentSlot, an AgentChip, a bulk bar's button — any element that takes a
ref and an onClick. The D key opens it through open with the trigger as its anchor. |
onDelegate* | (value: DelegatePopoverValue) => void | Promise<void> | — | Delegate with the chosen agent, instructions and finish rule. A returned promise keeps the popover open (and the button busy) until it settles. |
align | "center" | "end" | "start" | "start" | Where it aligns against the trigger. |
className | string | — | Classes for the popover. |
defaultAgentId | string | null | the only agent that can take it, if there is one | The agent chosen when it opens (Change agent: the current one). |
defaultInstructions | string | "" | The instructions it opens with. |
defaultWhenFinished | string | the first option | The "When finished" choice it opens with. |
instructionsMaxLength | number | 2000 | The instructions' maximum length. |
labels | Partial<DelegatePopoverLabels> | — | Replace any of the strings (DelegatePopoverLabels). |
loading | boolean | false | The agents are loading: skeleton rows in the list. |
onConnect | (() => void) | — | Open the place to connect an agent; the link (and the empty state's button) show when set. |
onOpenChange | ((open: boolean) => void) | — | Called when it opens or closes. |
open | boolean | — | Open state (controlled). |
title | React.ReactNode | "Delegate to an agent" | The title. Bulk: "Delegate 40 rows to…". |
whenFinishedOptions | readonly DelegatePopoverOption[] | — | "When finished" choices — tasks only; leave out for rows and bulk. |
Data attributes and CSS variables on DelegatePopover
| Attribute | Values |
|---|---|
data-icon | "inline-end" | "inline-start" |
data-loading | "" |
data-slot | "delegate-popover" | "delegate-popover-empty" | "delegate-popover-finish" | "delegate-popover-footer" | "delegate-popover-form" | "delegate-popover-loading" | "delegate-popover-submit" |
DelegatePopoverAgent
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | — | A stable id. |
name* | string | — | The agent's name: "Claude Code". |
access | string | null | — | What it may do, as its second line: "Read & write · Tasks". |
disabledReason | string | null | — | Why it can't take this ("Can't — read only"): listed, disabled, with the reason as its second line. |
image | string | null | — | Its image, under the purple bot. |
via | string | null | — | The member it acts for, when the list mixes owners. |
Accessibility
- The popover is a dialog named by its title; focus starts in the search field and returns to the trigger when it closes.
- The agents are a listbox: the chosen agent is checked and said as selected; a disabled agent keeps its reason as its second line.
- The instructions and When finished are labelled fields. Delegate stays disabled until an agent is chosen, and is busy while a returned promise runs.
| Key | Action |
|---|---|
| ↑ / ↓ | Move through the agents from the search field. |
| Enter | Choose the highlighted agent. |
| Tab | Move to the instructions, When finished, buttons. |
| Ctrl + Enter | Delegate from the instructions (⌘ on a Mac). |
| Esc | Close and return focus to the trigger. |
| Contract | States tested |
|---|---|
| Behaviour | pick, disabled-reason, search, keyboard, instructions, when-finished, bulk, empty, loading, pending, connect, labels |
| Accessibility | labelled-fields, listbox-keyboard, disabled-option-reason, busy-submit, browser-accessibility-test |
| Visual | default, bulk, empty, loading |
Do / Don't
Agent Panel
An agent on a record as a collaborator — whom it acts for, its state and actions, instructions, latest messages, a reply box and runs with Undo.
Message Scroller
A chat transcript scroller that follows the live edge only while the reader is, anchors each new turn near the top, and never pulls them away.