Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7.9 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 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)
This is the canonical source for UI rendering and validation.
Each returned item includes:
id→ the runtime tool identifier used by approvalslabel→ human-readable label for the UIkind→mcp,lsp, ormixedproviderIdsproviderNames
Do not re-derive tool IDs in the renderer.
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 prunes stale tool IDs from patterns and sessions
- 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
listApprovalToolDefinitions(...)helper
- canonical
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 + pruning for stale tool IDs
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
1. Pattern editor: default tool auto-approval
In PatternEditor, add a section that renders listApprovalToolDefinitions(workspace.settings.tooling).
Recommended behavior:
- show every available approval tool with a toggle
- write the selected tool IDs to
pattern.approvalPolicy.autoApprovedToolNames - keep this separate from the existing
tool-call/final-responsecheckpoint toggles - if there are no tools, show an empty state instead of an interactive list
2. Activity panel: per-session override
In the right-side Activity panel, add a per-session auto-approval section for tools.
Recommended behavior:
- base the list on
listApprovalToolDefinitions(workspace.settings.tooling) - show the current effective state
- distinguish inherited vs overridden state in the UI
- allow resetting the session to inherit the pattern defaults
- call
updateSessionApprovalSettings(...)when the user changes the override
Suggested UX:
Inheriting pattern defaultsbadge/state whensession.approvalSettingsisundefinedCustom for this sessionbadge/state when the override object existsReset to patternaction that callsupdateSessionApprovalSettings({ sessionId })
3. Disable while running
Match the current tools behavior:
- disable the per-session override controls while
session.status === 'running' - scratchpad sessions should show a non-interactive explanation because tool auto-approval does not apply there
4. Surface tool context in approval UI
Optional but recommended:
- show
approval.toolNamein the active approval banner / queued approval list when present - this is now available for tool-specific approval requests
5. Avoid ID duplication logic in the renderer
Important:
- do not manually rebuild LSP tool IDs in UI code
- use the shared helper output and persist the
idvalues from it
Validation commands
Backend changes were validated with:
bun run typecheckbun testbun run sidecar:testbun run build