ADK.Tool.Approval (ADK v0.0.1-alpha.1)

Copy Markdown View Source

GenServer that manages pending human approval requests for tool calls.

When ADK.Policy.HumanApproval intercepts a sensitive tool call, it registers a request here and blocks until a decision is made. External actors (CLI, LiveView, webhook handlers) call approve/2 or deny/3 to resolve pending requests.

Supervision

Start it in your supervision tree or via Application config:

# In your Supervisor:
{ADK.Tool.Approval, name: ADK.Tool.Approval}

# Or via ADK application config (starts with default name):
config :adk, start_approval_server: true

Usage

# From a policy (internal — use ADK.Policy.HumanApproval instead):
{request_id, request} = ADK.Tool.Approval.register("shell_command", %{"command" => "rm -rf /"})
decision = ADK.Tool.Approval.await(request_id, 60_000)
# => :allow | {:deny, reason}

# From a LiveView or CLI handler:
ADK.Tool.Approval.approve(request_id)
ADK.Tool.Approval.deny(request_id, "User clicked No")

# List pending (for a LiveView dashboard):
requests = ADK.Tool.Approval.list_pending()
# => [%{id: ..., tool_name: ..., args: ..., requested_at: ...}]

Summary

Functions

Approve a pending request by ID.

Block the calling process until request_id is approved, denied, or times out.

Deny a pending request by ID with an optional reason.

List all pending (unresolved) approval requests.

Register a new approval request. Returns {request_id, request_map}.

Start the Approval server.

Types

request()

@type request() :: %{
  id: String.t(),
  tool_name: String.t(),
  args: map(),
  requested_at: DateTime.t()
}

Functions

approve(server \\ ADK.Tool.Approval, request_id)

@spec approve(atom() | pid(), String.t()) :: :ok | {:error, :not_found}

Approve a pending request by ID.

await(server \\ ADK.Tool.Approval, request_id, timeout_ms \\ 60000)

@spec await(atom() | pid(), String.t(), pos_integer()) :: :allow | {:deny, String.t()}

Block the calling process until request_id is approved, denied, or times out.

Returns:

  • :allow — request was approved
  • {:deny, reason} — request was denied or timed out

deny(server \\ ADK.Tool.Approval, request_id, reason \\ "User denied")

@spec deny(atom() | pid(), String.t(), String.t()) :: :ok | {:error, :not_found}

Deny a pending request by ID with an optional reason.

list_pending(server \\ ADK.Tool.Approval)

@spec list_pending(atom() | pid()) :: [request()]

List all pending (unresolved) approval requests.

register(server \\ ADK.Tool.Approval, tool_name, args)

@spec register(atom() | pid(), String.t(), map()) :: {String.t(), request()}

Register a new approval request. Returns {request_id, request_map}.

Called by ADK.Policy.HumanApproval before blocking on await/3.

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Start the Approval server.