Classify every tool by reversibility. Read tools run freely; reversible writes run with logging; irreversible or costly actions (payments, deletions, emails to customers, on-chain transactions) require approval.
ts
const RISK = { search: "read", update_draft: "reversible", send_invoice_email: "approval" } as const;
if (RISK[call.name] === "approval") {
const id = await approvals.create({ runId, call, requestedBy: userId }); // durable row
return { status: "paused", approvalId: id }; // the loop stops here
}Persist the whole run state when pausing so the agent can resume hours later in a different process. Show the reviewer the exact arguments and the reasoning that produced them, and record who approved what. Batch-approving without visible arguments defeats the gate.
Exercise: add an approval gate to a destructive tool, then resume the run from the stored state after approval and confirm no step is re-executed twice.