A policy that pauses agent execution and requires human approval before running sensitive tools — Human-in-the-loop (HITL) tool confirmation.
Modes
:cli— prompts the user interactively via stdin. Blocks the agent process until the user typesy/yesorn/no. Best for terminal-based agents.:server— registers the request withADK.Tool.ApprovalGenServer and blocks until externally approved or denied. Use this for web UIs (LiveView), webhooks, or any async approval flow.
Usage
# CLI mode — user is prompted in the terminal
policy = ADK.Policy.HumanApproval.new(
sensitive_tools: ["shell_command", "delete_file"],
mode: :cli
)
# CLI mode with custom prompt function
policy = ADK.Policy.HumanApproval.new(
sensitive_tools: :all,
mode: :cli,
prompt_fn: fn %{tool_name: name, args: args} ->
IO.puts("About to call #{name} with #{inspect(args)}")
answer = IO.gets("OK? [y/N]: ") |> String.trim()
if answer in ["y", "yes"], do: :allow, else: {:deny, "Custom rejection"}
end
)
# Server mode — approve via ADK.Tool.Approval.approve/2
{:ok, _} = ADK.Tool.Approval.start_link(name: MyApprovals)
policy = ADK.Policy.HumanApproval.new(
sensitive_tools: ["delete_file"],
mode: :server,
server: MyApprovals,
timeout: 120_000
)
# Pass to Runner.run/5
ADK.Runner.run(runner, user_id, session_id, message, policies: [policy])Struct-based Policies
HumanApproval is a struct, not just a module. ADK.Policy.check_tool_authorization/4
detects structs and dispatches to check/4 directly rather than calling authorize_tool/3
on the module. This allows per-instance configuration (different tools, modes, servers).
If you use HumanApproval as a bare module (unusual), all tools are allowed by default.
Use new/1 to create a configured struct instance.
Summary
Functions
Check authorization for a specific HumanApproval struct instance.
Create a new HumanApproval policy struct.
Types
Functions
@spec check(t(), map(), map(), ADK.Context.t()) :: :allow | {:deny, String.t()}
Check authorization for a specific HumanApproval struct instance.
Returns :allow or {:deny, reason}. May block if awaiting human input.
Create a new HumanApproval policy struct.
Options
:sensitive_tools— list of tool name strings to intercept, or:all(required):mode—:cli(default) or:server:server—ADK.Tool.Approvalserver name or pid (server mode only):timeout— ms to wait for server-mode approval (default: 60_000):prompt_fn— custom(%{tool_name, args, request_id}) -> :allow | {:deny, reason}