Skip to contents

A Shiny module for reviewing the inputs of a tool call that is waiting for approval (see approval_dir in Agent and PermissionResultPending). The call stays paused until the reviewer acts. The module shows each argument with its declared type, description and proposed value, using tool_input_review(). The reviewer can edit simple fields, then approve or deny.

A field is editable when its declared type is enum, string, number, integer or boolean and its editor can show the proposed value exactly. Anything else, including a proposed value that doesn't fit its type (a boolean "yes", a number "abc"), is shown read-only as proposed; deny the call if it is wrong. An absent optional field stays absent unless the reviewer picks a value, or ticks "Provide a value" for text and numbers. Clearing a number removes the field. Approving with edits resumes with the edited input, which the tool must validate; approving without edits resumes with the original input. Denying runs nothing. Each approval is submitted at most once.

This is the input review step of Will Landau and Sam Parmar's trusted mini-agent pattern: a person checks the model-generated inputs before a trusted tool runs.

The server adds a Stop hook to agent so the review refreshes when a run pauses. Call it once per agent, while no run is active.

Usage

approval_review_ui(id)

approval_review_server(
  id,
  agent,
  decide = NULL,
  session = shiny::getDefaultReactiveDomain()
)

Arguments

id

Shiny module ID.

agent

An Agent configured with approval_dir.

decide

Optional function (path, decision, tool_input) that carries out the decision. The default calls agent$resume_approval(), which runs the rest of the agent's run in this R process and blocks the app until it finishes. To run it elsewhere, supply your own function that returns a promise, for example from mirai or promises::future_promise(). The module waits for the promise: on success it records the result and refreshes; on failure it records the error and allows a retry while the approval is still pending. The result is available from outcome().

session

Shiny session.

Value

approval_review_ui() returns a UI tag list. approval_review_server() returns a list of reactives: pending() (the current ApprovalContinuation or NULL), outcome() (a list with decision, tool_input, and result or error after the last decision), and a refresh() function.

Examples

if (interactive() && rlang::is_installed("bslib")) {
  library(shiny)
  ui <- bslib::page_fluid(approval_review_ui("review"))
  server <- function(input, output, session) {
    approval_dir <- tempfile("approvals-")
    dir.create(approval_dir)
    agent <- Agent$new(
      ellmer::chat("openai/gpt-6-luna"),
      approval_dir = approval_dir
    )
    approval_review_server("review", agent)
  }
}