Files
aryx/HANDOVER.md
T
David KayaandCopilot 130b114906 fix: use runtime tools for approval auto-approval
- 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>
2026-03-24 20:16:11 +01:00

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-response approvals still work the same way
  • tool-call checkpoints 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 rules list
  • a pattern may store tool auto-approval defaults even if tool-call checkpoints 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 defaults
  • session.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:

  1. Is tool-call approval enabled for this agent by approvalPolicy.rules?
  2. If not, auto-approve as before.
  3. If yes, is the runtime toolName in the effective autoApprovedToolNames list?
  4. If yes, auto-approve without creating a pending approval.
  5. 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().runtimeTools when 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 approvals
  • label → human-readable label for the UI
  • kindbuiltin, mcp, lsp, or mixed
  • providerIds
  • providerNames

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_fetch
  • view
  • glob

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_symbols
  • document_symbols
  • definition
  • hover
  • references

Example for profile ID ts:

  • lsp_ts_workspace_symbols
  • lsp_ts_document_symbols
  • lsp_ts_definition
  • lsp_ts_hover
  • lsp_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 autoApprovedToolNames to 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
  • 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
  • src/shared/contracts/ipc.ts
  • src/shared/contracts/channels.ts
  • src\preload\index.ts
  • src\main\ipc\registerIpcHandlers.ts
  • sidecar/src/Eryx.AgentHost/Contracts/ProtocolModels.cs
  • sidecar/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 typecheck
  • bun test
  • bun run sidecar:test
  • bun run build