Comma Agents
@comma-agents/daemonProtocol

Sandbox Permissions

The request_permission / permission_decision message pair for interactive sandbox authorization.

Sandbox Permissions

When a guard's policy chain returns "ask" for an access request, the daemon pauses the tool and asks the client for a permission decision via the onAsk callback. The client responds with allow, deny, allow-session, or deny-session, and execution resumes.

Event: request_permission

Sent by the daemon when a tool needs authorization. Broadcast to all subscribers of the run.

{
  "type": "request_permission",
  "runId": "run_abc123",
  "requestId": "perm-uuid-1234",
  "agentName": "writer",
  "toolName": "write_file",
  "operation": "fs.write",
  "resource": "/projects/app/src/index.ts",
  "reason": "policy-ask",
  "ts": "2025-01-15T10:30:01.000Z"
}
FieldTypeDescription
type"request_permission"Message type discriminator
runIdstringThe run that needs a permission decision
requestIdstringUnique ID for this request — echo back in permission_decision
agentNamestringName of the agent that triggered the operation
toolNamestring?Name of the tool that triggered the operation
operation"fs.read" | "fs.write" | "fs.exec" | "command.execute"Category of the operation
resourcestringResolved absolute path or command being accessed
reason"policy-ask" | "policy-deny-override"Why this request was raised
detailsRecord<string, unknown>?Optional extra context (e.g. content about to be written)
tsstringISO-8601 timestamp

reason values:

  • "policy-ask" — the guard's policy chain for this request returned "ask".
  • "policy-deny-override" — a session-level deny is in place, but the tool is requesting a one-time override.

Request: permission_decision

Sent by the client to deliver the decision. The runId and permissionRequestId must match the pending request_permission.

{
  "type": "permission_decision",
  "runId": "run_abc123",
  "permissionRequestId": "perm-uuid-1234",
  "decision": "allow-session"
}
FieldTypeRequiredDescription
type"permission_decision"yesMessage type discriminator
runIdstringyesThe run this decision is for
permissionRequestIdstringyesThe requestId from the matching request_permission
decision"allow" | "deny" | "allow-session" | "deny-session"yesThe client's decision
requestIdstringnoOptional correlation ID echoed on any error response

Decision semantics:

  • "allow" / "deny" — one-shot for this invocation only.
  • "allow-session" / "deny-session" — remembered in the tool's guard for the lifetime of this run (stored in-memory, not persisted to disk). Triggers a policy_updated broadcast for that tool.

On success, there is no direct response — the guard unblocks and the tool continues execution.

If no pending request matches the permissionRequestId, the daemon replies with an error:

{
  "type": "error",
  "code": "NO_PENDING_PERMISSION",
  "message": "No pending permission request perm-uuid-1234 for run run_abc123",
  "ts": "2025-01-15T10:30:02.000Z"
}

Event: policy_updated

Broadcast to all subscribers of the run whenever a tool's guard policy chain changes. This happens after "allow-session" / "deny-session" decisions and after update_policy messages. Each tool's guard emits its own event.

{
  "type": "policy_updated",
  "runId": "run_abc123",
  "tool": "write_file",
  "policies": [
    { "name": "forbidden-globs" },
    { "name": "write-path-policy" },
    { "name": "session-allow-write-generated---json" }
  ],
  "ts": "2025-01-15T10:30:02.500Z"
}
FieldTypeDescription
type"policy_updated"Message type discriminator
runIdstringThe run whose guard policy changed
toolstringWhich tool's guard emitted the change
policies{ name: string }[]Current policy chain (ordered)
tsstringISO-8601 timestamp

Request: update_policy

Sent by the client to add a policy to a tool's guard chain. Useful for pre-approving or pre-denying paths before execution reaches them. If tool is omitted, the policy is applied to all existing guards.

{
  "type": "update_policy",
  "runId": "run_abc123",
  "tool": "read_file",
  "mode": "read",
  "allow": ["generated/**", "tmp/**"]
}
FieldTypeRequiredDescription
type"update_policy"yesMessage type discriminator
runIdstringyesThe run whose guard to update
toolstringnoTool to target. Omitted = all existing guards
mode"read" | "write"yesWhich policy dimension to update
allowstring[]noGlob patterns to add to the allow list
denystring[]noGlob patterns to add to the deny list
default"allow" | "deny" | "ask"noReplace the default decision for this dimension

On success, the daemon applies the patch and broadcasts policy_updated to all subscribers.

If the run is not found, the daemon replies with an error:

{
  "type": "error",
  "code": "RUN_NOT_FOUND",
  "message": "No active run found for runId run_abc123",
  "ts": "2025-01-15T10:30:02.000Z"
}

Sequence Diagram

Client                          Daemon
  |                               |
  |  prepare_run -> start_run     |
  |------------------------------>|
  |                               |
  |     strategy_started          |
  |<------------------------------|
  |                               |
  |    request_permission         |
  |  (operation: "fs.write",      |
  |   resource: "/app/src/...",   |
  |   reason: "policy-ask")       |
  |<------------------------------|
  |                               |
  |  permission_decision          |
  |  (decision: "allow-session")  |
  |------------------------------>|
  |                               |
  |     policy_updated            |
  |  (tool: "write_file")         |
  |<------------------------------|
  |                               |
  |  agent_output, ...            |
  |<------------------------------|
  |                               |
  |     strategy_completed        |
  |<------------------------------|

On this page