Skip to content

Make an action consequential

A consequential action is one whose effect the user should own. Pick one of two shapes with the consequential prop.

Pass consequential. When the assistant calls the action, the element is scrolled into view with a ring around it and the label above or below it. The user presses it, or dismisses the ring. From examples/app/app/orders/[id]/page.tsx:

<AssistedAction name="refund_order" label="Refund this order" consequential>
<Button
variant="destructive"
disabled={!refundable}
onClick={() => shop.call("refundOrder", [order.id])}
>
Refund
</Button>
</AssistedAction>

The model sees the action marked “(consequential)”. Its call answers Rang "<label>" for the user; pressing it is theirs. A disabled or missing element answers so, and nothing rings.

Pass consequential="confirm". When the assistant calls the action, the panel shows a card with the label and Approve and Deny. On approval the action is pressed. A denial is final for that call. From examples/app/app/orders/page.tsx:

<AssistedAction
name="cancel_order"
id={order.id}
label={`Cancel ${order.customer}'s order #${order.id}`}
consequential="confirm"
>
<Button
size="sm"
variant="outline"
onClick={() => shop.call("cancelOrder", [order.id])}
>
Cancel
</Button>
</AssistedAction>

The model sees the action marked “(asks first)”. In a group, the card asks about the one row the assistant picked. The element is checked again after approval, in case it left or became disabled while the card waited.

The card is unstyled markup on data-assisted-confirm. The registry’s assisted-cards item styles it. See Install the panel.

A tool has no element to ring. consequential: true on useAssistedTool asks in the panel and runs execute only on approval:

export function Refund({ refund }: { refund: () => Promise<void> }) {
useAssistedTool("refund_order", {
description: "Refund this order.",
parameters: { type: "object", properties: {} },
consequential: true,
execute: async () => {
await refund();
return "Refunded the order.";
},
});
return null;
}

The card shows the tool’s description.

useRing returns a function that rings an element. The ring leaves when the component unmounts:

export function WhereToSave() {
const ringAt = useRing();
const save = useRef<HTMLButtonElement>(null);
return (
<>
<Button ref={save}>Save</Button>
<Button
variant="ghost"
onClick={() => save.current && ringAt(save.current, { message: "Save is here" })}
>
Where do I save?
</Button>
</>
);
}

ring(element, { message }) does the same outside React and returns a function that dismisses it.