- load runtime tools dynamically from Copilot CLI capabilities via tools.list - merge runtime tools with configured MCP and LSP tools in the approval catalog - keep a fallback builtin runtime tool list when capabilities are unavailable - move approval-tool pruning to the app service so dynamic tools are not dropped on load - update approval UI and docs to use the corrected runtime-tool model Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8.2 KiB
Tool auto-approval backend handover
What changed
The backend now supports tool-specific auto-approval defaults on patterns plus per-session overrides.
This builds on the existing approval-checkpoint system:
final-responseapprovals still work the same waytool-callcheckpoints still decide whether a tool call needs approval at all- the new feature decides which known runtime tools can be auto-approved instead of surfacing a manual approval
The approval queue work from earlier is unchanged and still applies.
Shared model
Pattern-level defaults
Pattern defaults now live on pattern.approvalPolicy.autoApprovedToolNames.
interface ApprovalPolicy {
rules: ApprovalCheckpointRule[];
autoApprovedToolNames?: string[];
}
Important:
- this list stores runtime tool identifiers, not display labels
- it is independent from the
ruleslist - a pattern may store tool auto-approval defaults even if
tool-callcheckpoints are currently disabled
If tool-call approvals are not enabled for the relevant agent, the tool auto-approval list has no runtime effect.
Per-session override
Sessions now support an optional override object:
interface SessionApprovalSettings {
autoApprovedToolNames: string[];
}
interface SessionRecord {
approvalSettings?: SessionApprovalSettings;
}
Semantics:
session.approvalSettings === undefined→ inherit the pattern defaultssession.approvalSettings.autoApprovedToolNames = []→ explicit session override that auto-approves nothing- non-empty
autoApprovedToolNames→ explicit session override list
Session overrides replace the pattern's tool auto-approval list for that session. They do not merge.
Runtime behavior
The main process now applies session approval overrides before sending the pattern to the sidecar.
That means the sidecar sees the effective approval policy for the session:
- pattern rules
- pattern defaults when no session override exists
- session override list when present
Actual decision flow for a tool call is now:
- Is
tool-callapproval enabled for this agent byapprovalPolicy.rules? - If not, auto-approve as before.
- If yes, is the runtime
toolNamein the effectiveautoApprovedToolNameslist? - If yes, auto-approve without creating a pending approval.
- Otherwise, emit a normal approval request and pause for user action.
Unknown or non-tool-specific permission requests still require manual approval when tool-call checkpoints are active.
Tool identity contract
Use the shared helper in src/shared/domain/tooling.ts:
listApprovalToolDefinitions(workspace.settings.tooling, sidecarCapabilities?.runtimeTools)
This is the canonical source for UI rendering and validation.
Important:
- runtime tools should come from
describeSidecarCapabilities().runtimeToolswhen available - the shared helper falls back to a conservative built-in catalog until capabilities load or when the CLI cannot report tools
- the helper also merges configured MCP server tools and derived LSP tools
Each returned item includes:
id→ the runtime tool identifier used by approvalslabel→ human-readable label for the UIkind→builtin,mcp,lsp, ormixedproviderIdsproviderNames
Do not re-derive tool IDs in the renderer.
Runtime tool IDs
For Copilot CLI runtime tools, the ID is the tool name returned by the sidecar capability payload.
Examples:
web_fetchviewglob
Descriptions come from the sidecar when available and can be surfaced in the UI.
MCP tool IDs
For MCP tools, the runtime ID is the raw tool name from server.tools.
Example:
{
id: "git.status",
label: "git.status",
kind: "mcp"
}
LSP tool IDs
For LSP tools, the runtime ID is derived from the profile ID and operation name.
Current operations:
workspace_symbolsdocument_symbolsdefinitionhoverreferences
Example for profile ID ts:
lsp_ts_workspace_symbolslsp_ts_document_symbolslsp_ts_definitionlsp_ts_hoverlsp_ts_references
Again: the renderer should consume the helper output instead of rebuilding these strings manually.
New IPC
Renderer access is now available through:
updateSessionApprovalSettings({
sessionId: string;
autoApprovedToolNames?: string[];
}): Promise<WorkspaceState>
Semantics:
- omit
autoApprovedToolNamesto clear the override and inherit the pattern defaults - pass
[]to keep an explicit session override with no auto-approved tools - pass a populated array to set an explicit session override list
The existing savePattern(...) path is still used for pattern defaults.
Validation and repair rules
The backend now enforces and maintains tool references consistently:
- saving a pattern rejects unknown
autoApprovedToolNames - updating session approval settings rejects unknown tool IDs
- scratchpad sessions reject non-empty session tool auto-approval overrides
- workspace load normalizes approval state and the app service prunes stale tool IDs after runtime tools/capabilities are available
- saving or deleting MCP/LSP definitions also prunes stale tool IDs from patterns and sessions
This is intentional: stored approval defaults should only reference tools that still exist.
Approval request payload changes
When the sidecar can identify the specific tool, approval-requested events now include toolName.
Tool-call approval titles/details are also more specific when the tool is known.
Examples:
- title:
Approve lsp_ts_hover - detail:
Primary requested custom tool permission for tool "lsp_ts_hover" in Copilot session ...
Existing UI can keep working without changes, but the UX agent can now surface better tool context.
Files changed
src/shared/domain/approval.ts- pattern defaults, session override model, effective-policy helpers, pruning/validation helpers
src/shared/domain/tooling.ts- canonical merged runtime/MCP/LSP
listApprovalToolDefinitions(...)helper
- canonical merged runtime/MCP/LSP
src/shared/contracts/sidecar.ts- runtime tool capability payload
src/shared/domain/session.ts- session approval settings + effective pattern merge helper
src/main/EryxAppService.ts- pattern validation, session override IPC handler, tooling cleanup, effective-pattern merge
src/main/persistence/workspaceRepository.ts- load-time normalization without prematurely pruning dynamic runtime tool IDs
sidecar/src/Eryx.AgentHost/Services/SidecarProtocolHost.cs- dynamic Copilot CLI runtime tool discovery via
tools.list
- dynamic Copilot CLI runtime tool discovery via
src/shared/contracts/ipc.tssrc/shared/contracts/channels.tssrc\preload\index.tssrc\main\ipc\registerIpcHandlers.tssidecar/src/Eryx.AgentHost/Contracts/ProtocolModels.cssidecar/src/Eryx.AgentHost/Services/CopilotWorkflowRunner.cs- tool-name extraction from permission requests
- tool-specific auto-approval decision
UX work for the frontend agent
All UX tasks from this section have been implemented in commit 4ff5cbb.
1. Pattern editor: default tool auto-approval — ✅ Done
PatternEditor renders listApprovalToolDefinitions(workspace.settings.tooling, sidecarCapabilities?.runtimeTools) with toggles writing to pattern.approvalPolicy.autoApprovedToolNames. Runtime tools come from the sidecar when available and fall back to the shared built-in catalog until capabilities load.
2. Activity panel: per-session override — ✅ Done
Right-side Activity panel shows "Inheriting pattern defaults" / "Custom for this session" badge with "Reset to pattern" action. Toggle rows for each tool call updateSessionApprovalSettings(...).
3. Disable while running — ✅ Done
Per-session override controls disabled when session.status === 'running'. Scratchpad shows non-interactive explanation.
4. Surface tool context in approval UI — ✅ Done
approval.toolName displayed in both the active approval banner and queued approval list when present.
5. Avoid ID duplication logic in the renderer — ✅ Done
All tool ID resolution uses listApprovalToolDefinitions() output exclusively.
Validation commands
Backend changes were validated with:
bun run typecheckbun testbun run sidecar:testbun run build