Xzero UI
Pro library

Agent approvalsPro

One approval card for agents: the waiting tool call reads like a permission prompt with the tool's mark, the agent's reason and its arguments, and you approve, deny or allow it from now on.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/agent-approvals
Live · keyboard ready

Pass requests and onDecide to connect your agent's permission flow; decisions can apply once or always. With no props it plays a sample queue.

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/agent-approvals

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { AgentApprovals } from "@/components/xzero/blocks/agent-approvals/agent-approvals";

export function ApprovalPrompt({ pending, rules }) {
  return (
    <AgentApprovals
      requests={pending}
      rules={rules}
      reviewer="Emma Collins"
      workspace="Northwind"
      onDecision={decision => fetch(`/api/approvals/${decision.requestId}`, { method: "POST", body: JSON.stringify(decision) }).then(response => {
        if (!response.ok) throw new Error("The agent did not get your decision. Nothing ran.");
      })}
      onRuleChange={({ type, rule }) => type === "added" ? saveRule(rule) : deleteRule(rule.id)}
    />
  );
}

// One pending request
const request = {
  id: "req-deploy-web",
  agent: { id: "deploy", name: "Deploy agent", model: "Claude Opus 5.5", provider: "anthropic" },
  tool: { id: "vercel.deploy", name: "Vercel", action: "Deploy to production", brand: "vercel" },
  title: "Run `vercel deploy --prod`",
  summary: "Deploy northwind-web to production",
  reason: "The preview passed all 214 checks and the release window opened at 14:00.",
  args: [
    { key: "project", label: "Project", value: "northwind-web", mono: true },
    { key: "commit", label: "Commit", value: "8f3c2a1", mono: true, editable: false },
    { key: "target", label: "Target", value: "production", mono: true },
  ],
  risk: "high",
  requestedAt: "2026-10-20T14:30:00",
};

API reference

AgentApprovals

One approval card for agents. The request on top of the queue reads like an inline permission prompt: the agent's name with its model's mark, how long ago it asked, the tool's real mark with the call in plain words (Run vercel deploy --prod, Send an email with Gmail), the agent's one line reason, the arguments as a key and value list whose values can be edited in place, and the risk as an icon plus Low, Medium or High. Deny, Approve (Enter) and an Always allow menu (this agent, or any agent in the workspace) decide it. The other pending requests wait below as one line rows; clicking a row brings it into focus, and a decision slides the next one up. Always allow sends a rule chip up into the Rules button, whose count rolls, and other waiting requests the new rule covers resolve themselves with a short note. A quiet Rules popover lists the rules with remove buttons, and Resolved expands in place into a log of who decided what and when.

PropTypeDefaultDescription
requestsApprovalRequest[]sample queuePending tool calls in queue order: { id, agent: { id, name, model, provider?: "anthropic" | "openai" | "google" }, tool: { id, name, action, brand? }, title, summary, reason, args, risk: "low" | "medium" | "high", requestedAt (ISO) }. The first one is in focus. Ids added to a later array join the end of the queue. Leave unset for the simulated demo.
rulesApprovalRule[]sample rulesAlways-allow rules that already skip the ask: { id, tool, scope: "agent" | "workspace", agent?: { id, name }, createdBy?, createdAt? }. A rule covers a request with the same tool id, and the same agent when its scope is agent.
resolvedResolvedEntry[]sample logEarlier decisions for the log, newest first: { id, agentName, tool, summary, outcome: "approved" | "denied" | "allowed", edited?, decidedBy, decidedAt }.
reviewerstring"Emma Collins"The person deciding. Recorded as decidedBy in each decision and in the log.
workspacestring"Northwind"Workspace name for the workspace-wide Always allow option and the rules list.
titlestring"Approvals"Heading of the card.
onDecision(decision: ApprovalDecision) => void | Promise<unknown>–Called for every decision with { requestId, outcome, args (edits applied), edited, ruleId?, decidedBy, decidedAt }, including requests a new rule covered (outcome "allowed"). Return a promise to keep the request in its sending state; a rejection shows the error with Retry and Deny, using the Error's message when it has one. Without it, delivery is simulated and the card says so.
onRuleChange(change: { type: "added" | "removed"; rule: ApprovalRule }) => void–Called when Always allow adds a rule or one is removed from the Rules popover. Persist rules here.
classNamestring–Class on the root element.

ApprovalRequest

One pending tool call.

PropTypeDefaultDescription
titlestring–The call in plain words, shown large. Wrap code in backticks to set it in the mono face, for example "Run vercel deploy --prod" or "UPDATE 1,284 rows in PostgreSQL".
summarystring–A short label for the queue row and the log, for example "Deploy northwind-web to production".
reasonstring–The agent's one line reason for the call.
toolApprovalTool–{ id, name, action, brand? }. id is the permission rules match on (vercel.deploy); action names the permission in the rules list (Deploy to production); brand picks a built-in mark: vercel, gmail, postgresql, linear, github, sentry or stripe.
argsApprovalArgument[]–The arguments as rows: { key, label, value, editable? (default true), mono?, multiline? }. Editable values turn into a field on click; multiline ones edit in a growing textarea.
risk"low" | "medium" | "high"–Shown as a shield icon in the success, warning or danger color plus the word.

Keyboard interactions

KeysAction
j / kBring the next or previous pending request into focus.
a or EnterApprove the request in focus. Enter approves while focus is on the card, not on a button or field.
dDeny the request in focus.
Enter / EscapeIn an argument field, Enter saves and Escape restores the value (Command or Control with Enter in a multiline field). Focus returns to the value.
TabMoves through the Rules button, the editable values, Deny, Always allow and Approve, the queue rows and the Resolved toggle.
Arrow keys, EscapeInside the Always allow menu and the Rules popover, move between items and close, with focus back on the trigger.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Guidance for AI tools

Blocks are complete, self-contained screens with sample data. Replace the sample data and connect the callbacks described above. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components