Tool Use Authorization
When agents request permission to execute tools (file operations, terminal commands, etc.), the extension provides a user approval mechanism. This chapter describes how authorization requests flow through the system and how per-agent policies are enforced.
Architecture
The authorization flow bridges three layers:
Agent (ACP requestPermission) → Extension (Promise-based routing) → Webview (MynahUI approval card)
The extension acts as the coordination point:
- Receives synchronous
requestPermissioncallbacks from the ACP agent - Checks per-agent bypass settings
- Routes approval requests to the webview when user input is needed
- Blocks the agent using promises until the user responds
Authorization Flow
With Bypass Disabled
sequenceDiagram
participant Agent
participant Extension
participant Settings
participant Webview
participant User
Agent->>Extension: requestPermission(toolCall, options)
Extension->>Settings: Check agents[agentName].bypassPermissions
Settings-->>Extension: false
Extension->>Extension: Generate approval ID, create pending promise
Extension->>Webview: approval-request message
Webview->>User: Display approval card (MynahUI)
User->>Webview: Click approve/deny/bypass
Webview->>Extension: approval-response message
alt User selected "Bypass Permissions"
Extension->>Settings: Set agents[agentName].bypassPermissions = true
end
Extension->>Extension: Resolve promise with user's choice
Extension-->>Agent: return RequestPermissionResponse
With Bypass Enabled
sequenceDiagram
participant Agent
participant Extension
participant Settings
Agent->>Extension: requestPermission(toolCall, options)
Extension->>Settings: Check agents[agentName].bypassPermissions
Settings-->>Extension: true
Extension-->>Agent: return allow_once (auto-approved)
Promise-Based Blocking
The ACP SDK's requestPermission callback is synchronous - it must return a Promise<RequestPermissionResponse>. The extension creates a promise that resolves when the user responds:
async requestPermission(params) {
// Check bypass setting first
if (agentConfig.bypassPermissions) {
return { outcome: { outcome: "selected", optionId: allowOptionId } };
}
// Create promise that will resolve when user responds
const promise = new Promise((resolve, reject) => {
pendingApprovals.set(approvalId, { resolve, reject, agentName });
});
// Send request to webview
sendToWebview({ type: "approval-request", approvalId, ... });
// Return promise (blocks agent until resolved)
return promise;
}
When the webview sends approval-response, the extension resolves the promise:
case "approval-response":
const pending = pendingApprovals.get(message.approvalId);
pending.resolve(message.response); // Unblocks agent
This allows the agent to block on permission requests without blocking the extension's event loop.
Per-Agent Settings
Authorization policies are scoped per-agent in symposium.agents configuration:
{
"symposium.agents": {
"Claude Code": {
"command": "npx",
"args": ["@zed-industries/claude-code-acp"],
"bypassPermissions": true
},
"ElizACP": {
"command": "elizacp",
"bypassPermissions": false
}
}
}
Why per-agent? Different agents have different trust levels. A user might trust Claude Code with unrestricted file access but want to review every tool call from an experimental agent.
Scope: Settings are stored globally (VSCode user settings), so bypass policies persist across workspaces and sessions.
User Approval Options
When bypass is disabled, the webview displays three options:
- Approve - Allow this single tool call, continue prompting for future tools
- Deny - Reject this single tool call, continue prompting for future tools
- Bypass Permissions - Approve this call AND set
bypassPermissions = truefor this agent permanently
The "Bypass Permissions" option provides a quick path to trusted status without requiring manual settings edits.
Webview UI Implementation
The webview uses MynahUI primitives to display approval requests:
- Chat item - Approval request appears as a chat message in the conversation
- Buttons - Three buttons (Approve, Deny, Bypass) using MynahUI's button status colors
- Tool details - Tool name, parameters (formatted as JSON), and any available metadata
- Card dismissal - Cards auto-dismiss after the user clicks a button (
keepCardAfterClick: false)
The specific MynahUI API usage is documented in the MynahUI GUI reference.
Approval Request Message
Extension → Webview:
{
type: "approval-request",
tabId: string,
approvalId: string, // UUID for matching response
agentName: string, // Which agent is requesting permission
toolCall: {
toolCallId: string, // ACP tool call identifier
title?: string, // Human-readable tool name (may be null)
kind?: ToolKind, // "read", "edit", "execute", etc.
rawInput?: object // Tool parameters
},
options: PermissionOption[] // Available approval options from ACP
}
Approval Response Message
Webview → Extension:
{
type: "approval-response",
approvalId: string, // Matches approval-request
response: {
outcome: {
outcome: "selected",
optionId: string // Which option was chosen
}
},
bypassAll: boolean // True if "Bypass Permissions" clicked
}
Design Decisions
Why block the agent? Tool execution should wait for user consent. Continuing execution while waiting for approval would allow the agent to make progress on non-tool operations, potentially creating race conditions where the user approves a tool call that's no longer relevant.
Why promise-based? JavaScript promises provide natural blocking semantics. The extension can return immediately (non-blocking event loop) while the agent perceives the call as synchronous (blocking until approval).
Why store in settings? Bypass permissions should persist across sessions. VSCode settings provide durable storage with UI for manual editing if needed.
Why auto-dismiss cards? Once the user responds, the approval card is no longer actionable. Dismissing it keeps the conversation history clean and focused on the actual work.
Future Enhancements
Potential extensions to the authorization system:
- Per-tool policies - Trust specific tools (e.g., "always allow Read") while prompting for others
- Resource-based rules - Auto-approve file reads within certain directories
- Temporary sessions - "Bypass for this session" option that doesn't persist
- Approval history - Log of past approvals for security auditing
- Batch approvals - Approve multiple pending tool calls at once