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:
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:
#[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:
cos serve --approval-timeout 120GET /api/approvals lists the ids of approvals that are waiting.