feat: category-based approval for runtime tools

Replace 20+ individual runtime tool toggles with 5 permission categories
(read, write, shell, web_fetch, store_memory) matching how Copilot CLI,
Cline, and other coding agents handle approvals.

- Add resolveApprovalToolKey() to map permission kinds to category IDs
- Simplify fallbackRuntimeApprovalTools to 5 categories
- Thread alwaysApprove through PendingApprovalHandle → sidecar resolve
- Update ApprovalBanner to use category labels for Always Approve button
- Update ResolveApprovalCommand contract with alwaysApprove field
- Update tests for category-based model

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-27 21:36:27 +01:00
co-authored by Copilot
parent f04e3b9dcc
commit 689b335220
8 changed files with 93 additions and 72 deletions
+22
View File
@@ -193,6 +193,28 @@ export function approvalPolicyRequiresCheckpoint(
return rule.agentIds.includes(normalizedAgentId);
}
const runtimePermissionKinds: ReadonlySet<string> = new Set(['read', 'write', 'shell', 'memory', 'url']);
/**
* Resolves the canonical approval key for a pending approval.
*
* Runtime tools always use their permission-kind category (`read`, `write`, `shell`)
* mapped to the corresponding approval-tool id. MCP/custom/hook tools use their
* specific tool name.
*/
export function resolveApprovalToolKey(
toolName: string | undefined,
permissionKind: string | undefined,
): string | undefined {
if (permissionKind && runtimePermissionKinds.has(permissionKind)) {
if (permissionKind === 'memory') return 'store_memory';
if (permissionKind === 'url') return 'web_fetch';
return permissionKind;
}
return toolName;
}
export function approvalPolicyAutoApprovesTool(
policy: ApprovalPolicy | undefined,
toolName?: string,
+10 -35
View File
@@ -137,42 +137,16 @@ export function resolveToolLabel(toolId: string): string {
return builtinToolLabels.get(toolId) ?? toolId;
}
// Fallback runtime tools used before sidecar capabilities are loaded or when the
// CLI cannot report its built-in tool catalog dynamically.
// Fallback approval tool categories for runtime tools. The approval UI groups
// built-in tools by permission category (read, write, shell, web, memory) rather
// than listing 20+ individual tools, matching other coding agents (Copilot CLI,
// Cline, Roo Code, etc.).
const fallbackRuntimeApprovalTools: ReadonlyArray<RuntimeToolDefinition> = [
// Permission-kind approval categories (used by sidecar for runtime tool session approval)
{ id: 'shell', label: 'Shell commands' },
{ id: 'read', label: 'Read files' },
{ id: 'write', label: 'Write files' },
// Shell tools (bash variants — SDK reports these on all platforms)
{ id: 'bash', label: 'Execute shell commands' },
{ id: 'read_bash', label: 'Read shell output' },
{ id: 'write_bash', label: 'Write shell input' },
{ id: 'stop_bash', label: 'Stop shell session' },
{ id: 'list_bash', label: 'List shell sessions' },
// File tools
{ id: 'view', label: 'View files' },
{ id: 'create', label: 'Create files' },
{ id: 'edit', label: 'Edit files' },
{ id: 'apply_patch', label: 'Apply patch' },
// Search tools
{ id: 'grep', label: 'Search file contents' },
{ id: 'glob', label: 'Find files by pattern' },
// Agent tools
{ id: 'task', label: 'Run sub-agent' },
{ id: 'read_agent', label: 'Read agent results' },
{ id: 'list_agents', label: 'List agents' },
// Web tools
{ id: 'web_fetch', label: 'Fetch web content' },
{ id: 'web_search', label: 'Search the web' },
// Other tools
{ id: 'skill', label: 'Invoke skill' },
{ id: 'show_file', label: 'Show file' },
{ id: 'fetch_copilot_cli_documentation', label: 'Fetch CLI docs' },
{ id: 'update_todo', label: 'Update checklist' },
{ id: 'shell', label: 'Shell commands' },
{ id: 'web_fetch', label: 'Web access' },
{ id: 'store_memory', label: 'Store memory' },
{ id: 'sql', label: 'Query session data' },
{ id: 'lsp', label: 'Language server' },
];
export function createWorkspaceSettings(): WorkspaceSettings {
@@ -238,12 +212,13 @@ export function normalizeSessionToolingSelection(
export function listApprovalToolDefinitions(
tooling: WorkspaceToolingSettings,
runtimeTools: ReadonlyArray<RuntimeToolDefinition> = fallbackRuntimeApprovalTools,
_runtimeTools: ReadonlyArray<RuntimeToolDefinition> = fallbackRuntimeApprovalTools,
): ApprovalToolDefinition[] {
const toolsById = new Map<string, ApprovalToolDefinition>();
const runtimeApprovalTools = runtimeTools.length > 0 ? runtimeTools : fallbackRuntimeApprovalTools;
for (const tool of runtimeApprovalTools) {
// Always use category-level approval for built-in runtime tools regardless of
// what the sidecar reports as individual tools.
for (const tool of fallbackRuntimeApprovalTools) {
registerApprovalTool(toolsById, {
id: tool.id,
label: resolveToolLabel(tool.id),