ADK.Policy.HumanApproval (ADK v0.0.1-alpha.1)

Copy Markdown View Source

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 types y/yes or n/no. Best for terminal-based agents.

  • :server — registers the request with ADK.Tool.Approval GenServer 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

t()

@type t() :: %ADK.Policy.HumanApproval{
  mode: :cli | :server,
  prompt_fn: (map() -> :allow | {:deny, String.t()}) | nil,
  sensitive_tools: [String.t()] | :all,
  server: atom() | pid(),
  timeout: pos_integer()
}

Functions

check(policy, tool, args, ctx)

@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.

new(opts)

@spec new(keyword()) :: t()

Create a new HumanApproval policy struct.

Options

  • :sensitive_tools — list of tool name strings to intercept, or :all (required)
  • :mode:cli (default) or :server
  • :serverADK.Tool.Approval server 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}