Tool Approvals
Query, display, and resolve unrestricted tool requests within a specific Session
Tool Approvals
An approval belongs to the Session that started the tool call, not to the top-level Agent. This prevents a decision from being submitted to the wrong conversation when Sessions run in parallel.
When a tool such as Shell requests extra permission, its Tool Part in the current Assistant Message enters approval-required.
ready -> approval-required -> running -> completed
\-> failedDisplay a live request and resolve it
An approval-required Tool Part carries the complete approval snapshot. The subscription only reports state; it does not expose a command channel:
const unsubscribe = session.subscribe((mutation) => {
if (
mutation.variant !== "part" ||
mutation.type !== "tool" ||
mutation.part.state !== "approval-required" ||
!mutation.part.approval
) {
return;
}
show_approval_request(mutation.part.approval);
});
// After user confirmation:
await session.resolve_approval({
approval_id,
decision: "approved",
});The only decisions are approved and denied. Keep subscribe() as a data-only stream and call resolve_approval() only in response to an explicit user action. The next Tool Part Mutation is the authoritative resulting state.
Query first, resolve later
An approval screen, a reconnected client, or a client without a continuous subscription should use Session methods:
const pending = await session.approvals();
for (const approval of pending) {
render_approval({
tool_name: approval.tool_name,
command: approval.command,
cwd: approval.cwd,
reason: approval.reason,
operation: approval.operation,
});
}
await session.resolve_approval({
approval_id: pending[0].approval_id,
decision: "denied",
});resolve_approval() only handles a pending request in the current Session. When the request is missing, already resolved by another client, or belongs to another Session, its result has success: false; it is never resolved across a Session boundary.
Approval modes
const current = await session.approval_mode();
await session.set_approval_mode({
mode: "always-allow",
});| Mode | Behavior |
|---|---|
ask | Default. Every unrestricted request enters the pending queue. |
always-allow | New future requests in this Session are automatically approved. |
always-allow does not change the sandbox type, affect other Sessions, or resolve requests that are already pending. A product should surface the active mode clearly and place elevated permission behind explicit user choice.
UI implementation details
- Use
approval_idas the stable approval-card key andtool_call_idto link it to the Tool Part. - Keep a tool output area waiting while the Part is
approval-required; do not mark it complete. - After submitting a decision, wait for the next Part Mutation to show
running,completed, orfailed; do not synthesize success client-side. - One request can have multiple subscribers;
resolve_approval()'ssuccessis authoritative. - On Session change, clear the old approval queue. Never send an old Session's
approval_idto a new Session.
See Live subscriptions for complete Tool Part state and rendering.