Most agent frameworks treat "the agent decided to do X" and "X happened" as the same event. For reversible actions — reading a file, drafting a message, running a query against a read replica — that's fine. For actions that can't be undone — deleting a record, sending an email, pushing to production, spending money — collapsing those two events into one is how teams end up debugging an incident that a human would never have approved if they'd been asked first.

The fix isn't "add a confirmation dialog." It's deciding, deliberately, which actions get a gate, who the gate asks, and what happens while it waits.

Classify actions by reversibility, not by risk

Teams often build their permission model around how dangerous an action sounds, which produces inconsistent gates: "delete_user" gets a confirmation, "update_billing_plan" doesn't, even though the second one is harder to reverse in practice once an invoice has gone out. A better first pass is to sort every tool an agent can call into three buckets: fully reversible (no gate needed), reversible with effort (a gate is optional, logging is mandatory), and irreversible or externally visible (a gate is mandatory, no exceptions). "Externally visible" matters as its own category — an action that's technically reversible on your side, like sending a Slack message, still isn't reversible in the eyes of whoever read it.

Once you have that classification, the interesting design work is in the middle bucket. Teams that skip it either over-gate (a confirmation on every tool call, which trains users to click "approve" without reading) or under-gate (no gate anywhere, which is how you get an agent that quietly rewrites a production config at 2 a.m.).

Design the wait state, not just the prompt

A confirmation gate is only as good as what happens during the pause. Three things need to be true for it to actually work.

The agent has to stop cleanly — not poll in a loop burning tokens, not silently continue down a fallback path while waiting, and not lose the context it built up when the human finally responds. That means the pause has to be a first-class state in your orchestration, not a side effect of blocking on stdin.

The human has to see enough to decide, not just a green "confirm" button. Show the actual payload the tool call would send, not a paraphrase of it — an agent summarizing its own action for approval is exactly the failure mode you're trying to gate against.

The default on timeout has to be "do nothing," never "proceed." An unanswered confirmation is not a confirmation. This sounds obvious until you're the one debugging why an agent auto-approved itself after a 30-second timeout someone set to keep a demo moving.

The gate is a contract, not a courtesy

The most durable version of this pattern treats the confirmation gate as part of the tool's contract, not as UI sugar layered on top after the fact. The tool schema itself should carry a field like requires_confirmation: true that the orchestration layer enforces centrally, so a new tool added six months from now inherits the right default instead of relying on whoever wires it up that day to remember the rule. Agents get swapped out, prompts get rewritten, but a schema-level gate survives both — which is exactly the property you want from the one control standing between an agent and something you can't take back.