Approvals

Tools that change the outside world wait for a person. Every decision is stored.

When a tool’s risk is write, the agent stops and asks before it runs. You answer in the dashboard or at the y/N prompt in cos chat. No answer within five minutes counts as a no.

How it works

The policy decides

The built-in approval_policy hook runs on pre_tool_call. It reads the tool’s risk:

Risk Result
read Continue: the tool runs
write AskUser("Allow <tool> to run?")
deny Block("<tool> is not allowed")

It is registered first, and the first Block always wins, so no plugin can approve past a deny. Plugin hooks can still add their own AskUser or Block.

The agent asks

pre_approval_request fires, then the agent hands an ApprovalRequest to the Approver:

Rust
pub struct ApprovalRequest {
    pub id: Uuid,
    pub turn: TurnInfo,
    pub tool_name: String,
    pub args: Value,
    pub prompt: String,
}

A person answers

The answer is approved, denied or timed_out. The agent applies its own timeout around the approver, so an approver never has to track time. post_approval_response fires with the choice.

The result is stored

The decision is stored in the approvals table next to its tool call. If it was not approved, the tool doesn’t run and the model gets Not approved (denied) or Not approved (timed_out) as an error result.

Approvers

Approver Where Behavior
Dashboard cos serve Shows the request in the chat. Answer with the buttons, or send {"type":"approve","id":"…","approved":true} over the WebSocket.
Terminal cos chat Prints <tool> <args> [y/N]. Only y, Y or yes approves.
DenyApprover Anywhere nobody can answer Denies everything. The safe default.
AutoApprover Tests only Approves everything.

Write your own by implementing one method:

Rust
#[async_trait]
pub trait Approver: Send + Sync {
    async fn request(&self, request: ApprovalRequest) -> ApprovalDecision;
}

The timeout

The default is 300 seconds. Change it for the dashboard with:

Terminal
cos serve --approval-timeout 120

GET /api/approvals lists the ids of approvals that are waiting.

Edit this page on GitHub

Esc