# Human-in-the-loop approval UI for AI agents

An agent that changes things should ask first. There are two kinds of asking. The person in the chat allows or denies a tool call before it runs. Or a second person approves a change someone else asked for, because nobody should approve their own.

For the first, the adapters (useCandorChat, useClaudeAgent, useAgUi) give you `approvals`, the tool calls waiting on a person, and `respond(id, approved, reason)` to answer one. PermissionPrompt draws one: why it reached the person, the run's permission mode, the input they can edit before allowing it, and "don't ask again" rules nobody has ticked. Or draw your own buttons where you want them.

For the second, ApprovalRequest shows what changes, where, what it touches, whether it can be undone, and a hash that ties the decision to exactly that change. ApprovalQueue lists the requests by what they need from the viewer. A person who can't decide sees why on the button.

Web page: https://candor.themethodic.com/patterns/ai-approval-ui

## Components

- **Permission prompt** (`permission-prompt`, free; https://candor.themethodic.com/docs/permission-prompt.md): One tool call waiting on the person: why it asked, the mode, edit before allowing, opt-in "always allow".
- **useCandorChat** (`use-candor-chat`, free; https://candor.themethodic.com/docs/use-candor-chat.md): Vercel AI SDK: the tool calls waiting on a person, and respond() to answer them.
- **useClaudeAgent** (`use-claude-agent`, free; https://candor.themethodic.com/docs/use-claude-agent.md): Claude Agent SDK: the same shape, answered through your approve route.
- **useAgUi** (`use-ag-ui`, free; https://candor.themethodic.com/docs/use-ag-ui.md): AG-UI: interrupts waiting on a person; answering them resumes the run.
- **Approval request** (`approval-request`, Pro; https://candor.themethodic.com/docs/approval-request.md): A request for second-person approval, tied to the exact change by a hash.
- **Approval queue** (`approval-queue`, Pro; https://candor.themethodic.com/docs/approval-queue.md): Requests grouped by what they need from the viewer, soonest to expire first.
- **Tool permissions** (`tool-permissions`, Pro; https://candor.themethodic.com/docs/tool-permissions.md): Per tool: always, ask first, or never.
- **Blast radius** (`blast-radius`, Pro; https://candor.themethodic.com/docs/blast-radius.md): What a step can reach, or a plain statement that it couldn't be measured.
- **Locked** (`locked`, free; https://candor.themethodic.com/docs/locked.md): A control that can't be used says why, for example on your own request.
- **Hold to confirm** (`hold-to-confirm`, Pro; https://candor.themethodic.com/docs/hold-to-confirm.md): A risky step the person holds for its full time; letting go cancels, and the reason says what it does and whether it can be undone.
- **Undo window** (`undo-window`, Pro; https://candor.themethodic.com/docs/undo-window.md): After the step: Undo for exactly as long as the server allows, then an honest "can't be undone now".

## Install

```bash
npx candor-ui add permission-prompt approval-request approval-queue locked
```

## Notes

- The UI shows a decision; your server enforces it. The approval-gated-change block does both: the agent proposes, a different person approves in a queue, and only the exact change they saw is applied.
- Decisions show only once they're recorded, so the screen never claims an approval the server doesn't have.
