Answering permission requests
When the CLI asks whether it may do something, the responder decides.
command.onPermission((request) => { if (request.subtype === 'can_use_tool') return request.approve(); return request.deny('user declined');});The reply is built on the request, because a reply is only meaningful paired with the request_id it answers — and that id arrived with the request.
Amending what was proposed
Section titled “Amending what was proposed”A host may also edit what the CLI proposed before allowing it, which is how a UI lets a user amend a command before it runs:
command.onPermission((request) => request.approve({ command: 'ls -la' }));Returning null leaves the request unanswered, which stalls the CLI. Do that only when something else will answer it.
The envelope is nested twice
Section titled “The envelope is nested twice”The payload that reaches the CLI is nested twice, and it has to be. The outer response is the delivery envelope — that a reply was produced, and which request it answers — while the inner one carries the decision:
{ "type": "control_response", "response": { "subtype": "success", "request_id": "req-7", "response": { "behavior": "allow", "updatedInput": { "command": "ls -la" } } }}Why deny() takes a reason
Section titled “Why deny() takes a reason”Where to go next
Section titled “Where to go next”- Permission modes — the five modes, and why a mode change means a respawn.
- Reference: events —
PermissionRequestEventin full.