Quick Start
1
Simple Usage
Set
approval: true to gate every tool behind an interactive y/n prompt on the terminal.2
With an ApprovalManager
Pass an
ApprovalManager to add auto-approve / auto-deny rules and a custom handler.approval accepts only boolean | ApprovalManager. Per-tool rules live on the ApprovalManager (via addAutoApprove / addAutoDeny), not on the agent.User Interaction Flow
How approval: true behaves
approval: true builds an ApprovalManager for you and wires createCLIApprovalPrompt() as its handler, so the default is a blocking y/n prompt on stdin.
Without a handler, requestApproval() waits on respond() — which nobody calls in a plain CLI session — so it would otherwise stall until the 5-minute default-deny timeout. Wiring the prompt is what keeps the default usable.
Denials are fed back to the model, not thrown. When a call is denied, the agent returns this string as the tool result so the model can course-correct and continue the run:
Correlation with toolInvocationId
The gate passes the LLM’s tool_call.id as toolInvocationId on every approval request. Handlers and out-of-band UIs use it to correlate a decision back to the exact pending call — never positionally. This keeps multiple in-flight tool calls disambiguated.
Custom Handler
Register handlers withonApprovalRequest. The first handler to resolve true approves; if all resolve false, the call is denied.
Configuration Options
ApprovalManager constructor options:
ApprovalManager instance methods:
Advanced
Per-tool wrapper: withApproval
Wrap a single tool so it gates itself. Throws ToolApprovalDeniedError on deny unless you provide onDenied.
Global manager: getApprovalManager / setApprovalManager
withApproval uses a process-global manager when none is passed. Configure it once.
Dangerous-pattern helpers
Pre-built regexes and checkers flag risky inputs so you can require approval only when needed.DANGEROUS_PATTERNS exposes fileDelete, dbDestructive, shellDangerous, and networkSensitive.
Events
ApprovalManager is an event emitter — subscribe with .on(...) to drive an out-of-band UI (e.g. a web dashboard that answers via respond()).
Errors
Two approval layers. The
ApprovalManager → ApprovalHandler layer is boolean-only (a handler returns true/false). A backend installed via setApprovalBackend (see below) produces a full ApprovalBackendDecision carrying scope ('once' | 'session' | 'always'), scopePattern, and feedback — Python parity with praisonaiagents.approval. The handler projects that decision down to a boolean for the manager.Approval Backends (Python parity)
Swap the defaulty/n prompt for a richer backend that matches praisonaiagents.approval — auto-approve, a diff-aware console, an LLM reviewer, or a legacy callback.
An Agent with approval: true builds an ApprovalManager; setApprovalBackend answers its requests.
ApprovalManager to install the backend on it instead of the process-global one.
Which backend?
AutoApproveBackend
Approves every call — use in bots, CI, or trusted unattended runs.Python parity:
praisonaiagents.approval.AutoApproveBackend — same always-approve behaviour.ConsoleBackend
Prompts on the terminal with a coloured unified diff and once/session/always scopes.y/yesis accepted asonce.- Empty input defaults to
n(deny). dprompts for free-text guidance returned ondecision.feedback.- Diffs render in ANSI unless
NO_COLORis set or output is not a TTY — override withnew ConsoleBackend({ colors: false }).
Python parity:
praisonaiagents.approval.ConsoleBackend — same prompt, Rich markup instead of ANSI.AgentApproval
Delegates the decision to another AI agent that repliesAPPROVE or DENY.
Python parity:
praisonaiagents.approval.AgentApproval(approver_agent=..., llm="gpt-4o-mini").CallbackBackend
Wraps a legacy(toolName, arguments, riskLevel) => decision | boolean callback.
Python parity:
praisonaiagents.approval.CallbackBackend(callback).Build your own backend
ImplementApprovalBackend — requestApproval (async) and requestApprovalSync. Use the approvalRequest / approvalDecision builders for Python-shaped defaults and suggestScopePattern for a reusable “always” pattern.
Wiring functions
setApprovalBackend installs a backend; getApprovalBackend reads it back.
ApprovalHandler, or convert a manager request into the Python shape, directly.
approvalRequest and approvalDecision build the Python-shaped objects with dataclass defaults (approvalId auto-fills like uuid4().hex).
Permission-target helpers
Turn a tool call into a reusable permission target or glob forsession/always scopes.
ARITY maps commands (git, npm, docker, …) to the number of leading tokens commandPrefix keeps.
API surface
Everything exported from theapproval module — all import flat from praisonai.
ApprovalBackendRequest — what a custom backend receives:
ApprovalBackendDecision — what a backend returns:
BackendHandlerOptions — passed to setApprovalBackend / approvalHandlerFromBackend:
Best Practices
Auto-approve safe actions
Auto-approve safe actions
Reading data is usually safe.
approver.addAutoApprove(/^(read|list|search)_/) skips the prompt for read-only tools.Auto-deny dangerous patterns
Auto-deny dangerous patterns
Auto-deny is checked first. Block deletions and destructive commands with
approver.addAutoDeny(/^delete_/).Set reasonable timeouts
Set reasonable timeouts
The default is 5 minutes and default-denies on timeout. Lower it for interactive flows:
new ApprovalManager({ defaultTimeout: 60_000 }).Correlate with toolInvocationId
Correlate with toolInvocationId
In UIs with concurrent calls, always route decisions by
request.toolInvocationId, never by order — positional matching authorises the wrong call.Reviewing task outputs, not tool calls? See Human-in-the-Loop Review for
Task(humanInput: true) and reviewTaskOutput().Related
Python Approval Backends
The matching Python/CLI backends
Approval Protocol
Cross-SDK backend matrix and request/decision shapes
Human Review
Approve a task’s output before the next task
Guardrails
Input/output validation

