Skip to content
Component installs need the registry setup
VegaStack Design

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
stable
Since
0.25.89
Accessibility pattern
labelled fields, listbox keyboard, disabled reasons shown

Last updated

Assignee
Priya Raman
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-popover

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/delegate-popover

Run 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
Priya Raman
Delegate

States

Bulk rows (their own title, no When finished), no agents connected, and agents loading.

API Reference

PropTypeDefaultDescription
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.
classNamestring—Classes for the popover.
defaultAgentIdstring | nullthe only agent that can take it, if there is oneThe agent chosen when it opens (Change agent: the current one).
defaultInstructionsstring""The instructions it opens with.
defaultWhenFinishedstringthe first optionThe "When finished" choice it opens with.
instructionsMaxLengthnumber2000The instructions' maximum length.
labelsPartial<DelegatePopoverLabels>—Replace any of the strings (DelegatePopoverLabels).
loadingbooleanfalseThe 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.
openboolean—Open state (controlled).
titleReact.ReactNode"Delegate to an agent"The title. Bulk: "Delegate 40 rows to…".
whenFinishedOptionsreadonly DelegatePopoverOption[]—"When finished" choices — tasks only; leave out for rows and bulk.

Data attributes and CSS variables on DelegatePopover

AttributeValues
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

PropTypeDefaultDescription
id*string—A stable id.
name*string—The agent's name: "Claude Code".
accessstring | null—What it may do, as its second line: "Read & write · Tasks".
disabledReasonstring | null—Why it can't take this ("Can't — read only"): listed, disabled, with the reason as its second line.
imagestring | null—Its image, under the purple bot.
viastring | 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.
KeyAction
↑ / ↓Move through the agents from the search field.
EnterChoose the highlighted agent.
TabMove to the instructions, When finished, buttons.
Ctrl + EnterDelegate from the instructions (⌘ on a Mac).
EscClose and return focus to the trigger.
ContractStates tested
Behaviourpick, disabled-reason, search, keyboard, instructions, when-finished, bulk, empty, loading, pending, connect, labels
Accessibilitylabelled-fields, listbox-keyboard, disabled-option-reason, busy-submit, browser-accessibility-test
Visualdefault, bulk, empty, loading

Do / Don't

Do
List every agent the viewer has, and disable the ones that can't take the work with their reason.
Don't
Hide an agent that can't take it, or open an empty dialog with a disabled button when no agent is connected.
Do
Use one verb, Delegate, for tasks and rows alike.
Don't
Offer Hand off and Take back on one surface and Delegate on another.

On this page