Skip to main content
This example shows the full human-in-the-loop approval cycle. An email-sending tool is marked requiresApproval: true. When the agent tries to call it, Vibes throws ApprovalRequiredError instead of executing the tool. Your code inspects the pending tool calls, approves them (or rejects), and resumes the agent with agent.resume().

What you’ll learn

  • Marking tools with requiresApproval: true
  • Catching ApprovalRequiredError and inspecting pending requests
  • Approving or rejecting tool calls
  • Resuming the agent with agent.resume()

Prerequisites

  • Vibes installed (jsr:@vibesjs/sdk)
  • ANTHROPIC_API_KEY environment variable set

Complete example

Source: examples/human-in-the-loop.ts

Run it

Example output:

How it works

requiresApproval: true

When the agent decides to call this tool, Vibes pauses execution before calling execute() and throws ApprovalRequiredError.

err.deferred.requests

An array of pending tool calls. Each has toolCallId (opaque ID), toolName, and args (typed per the tool’s Zod schema).

agent.resume(deferred, { results })

Continues the agent from the pause point. results must contain one entry per request, with the same toolCallId. The result string is returned to the model as the tool’s output - use "approved" or any descriptive string.

Rejection flow

To reject a tool call, pass a result string indicating rejection. The model receives this as the tool result and can respond accordingly (e.g., “I couldn’t send the email because it was rejected.”).

Next steps