Preview
Design guidance
Tool composes Activity row/disclosure parts backed by canonical Collapsible. ToolApproval is a separate decision surface. Direct approval children stay visible when execution details are closed. Approval actions use nested ButtonGroup owners for distinct Deny and Allow once controls and remain disabled while a decision is pending. Approved, denied, failed, and expired records remain honest; approval does not claim that execution succeeded. Errors use an alert region, and custom content keeps the caller's semantics.
Installation
Basic usage
The caller owns the decision state. Connect the callbacks to your application or runtime. An approved decision does not imply successful execution.
Structures
ToolApproval can stand alone or attach to Composer. Tool groups optional execution details; a direct approval child stays visible outside its collapsed detail.
Examples
Running
The running state names the operation and shows only caller-provided input.
Input
Searching the order history.Result details
The quiet result summary opens only when the user asks to inspect the returned details.
Approval
Approval is a separate decision. Approve or deny updates the caller-owned record immediately.
Approval with caller Composer
Place ToolApproval with layout="compact" in Composer.secondary. Supply the decision question through prompt and the affected file or action through children. The editor stays in the same compact LayerCard.
Failure and retry
The error remains visible and retry is available only when the caller supplies it.
Cancelled
Cancelled operations remain readable and do not claim completion.
Result
The lookup stopped before it changed anything.Custom result
Custom result content stays caller-rendered and is not serialized by Tool.
Result
Standalone approval
The decision remains visible without a disclosure.
Submitting a decision
Actions stay disabled until the caller acknowledges the decision.
Approval failure and retry
Retry returns to the pending request. A failed submission retains the request.
Expired approval
An expired request cannot send another decision.
Long approval
The affected path wraps while the two actions remain usable.
Vercel AI SDK
An actual installed SDK hook and deterministic native transport drive approval and output.
- Read the weekly update
- Check the destination
Input
docs/weekly-update.mddocs/weekly-update.mdTanStack AI
An actual installed TanStack hook and local AG-UI connection drive the same view contract.
- Read the weekly update
- Check the destination
Input
docs/weekly-update.mddocs/weekly-update.mdAPI reference
| Prop | Type | Default | Description |
|---|---|---|---|
name / state / part | string / ToolState / ToolPart-like data | Names the operation and declares its observable lifecycle state. | |
open / defaultOpen | boolean | false | Controls the composed disclosure. |
ToolApproval.layout | default | compact | default | Compact places the decision question and actions in one attached panel, with Deny before Allow once. |
ToolApproval.prompt | ReactNode | Shows the caller-owned decision question instead of the generic pending heading. | |
ToolApproval.approvalId | string | Identifies the current permission request so a new request invalidates late results. | |
ToolApproval.onApprove / onDeny / onRetry | () => void | Promise<void> | Routes caller-owned permission and recovery decisions. Tool does not execute them. | |
ToolInput / ToolOutput | children | Renders caller-owned, already-safe content without serializing unknown values. |
Import decisions from @/components/ui/tool-approval. Existing @/components/ui/tool imports remain compatible. Tool names an execution; ToolApproval names the decision. Approval acknowledgement does not imply a successful result. Optional adapter bindApproval({ approvalId, addToolApprovalResponse, getCurrent }) checks the current request identity/state before dispatch; the host owns authorization and persistence.