Approvals

  1. What is gated
  2. TUI
  3. Headless
  4. SDK

Model-authored run_command does not run until the host says so. Default is deny. Protocol 4 is required.

An allowed command runs as the unprivileged guest user (UID/GID 1000), not as the root-owned guest agent. Its dedicated process group is terminated when the command returns or is canceled, so allow_once cannot leave a background shell or replace /usr/local/bin/abox-guest for a later resume.

MCP tools, apply_patch, and the read-only builtins do not prompt. Host import of a guest patch is not built yet.

What is gated

Action Gate
Model calls run_command during Turn Host approval (allow_once or deny)
Session.RunCommand / abox --probe-vm None — supervisor RPC, not a model tool
MCP tool call None yet
apply_patch / list_files / read_file / search None

The guest sends request_run_command_approval with the command, workdir, and timeout. Anything other than allow_once (missing approver, error, canceled turn, unknown decision) is deny.

TUI

The TUI installs an approver that opens APPROVAL REQUIRED. Deny is focused. ←/k deny, →/j allow once, Enter confirms, Esc denies. There is no “remember for this session”.

Details: CLI and TUI.

Headless

abox exec uses the same agent and broker, but does not install an approver. Model run_command is denied. Probe with Session.RunCommand or abox --probe-vm if you need a supervisor shell in the guest.

SDK

err = sess.SetApprover(abox.ApproverFunc(func(ctx context.Context, req abox.ApprovalRequest) (abox.ApprovalDecision, error) {
    if req.Command == "uname -a" {
        return abox.ApprovalAllowOnce, nil
    }
    return abox.ApprovalDeny, nil
}))

SetApprover(nil) restores fail-closed. Cannot change the approver during a turn. Approvals example.

ApprovalRequest carries Tool (always run_command today), ToolID, Command, WorkDir, and TimeoutSec. Empty workdir means /work/repo.