From b5ab92e444900cd1bec9e3be58a5e2696aa4cab6 Mon Sep 17 00:00:00 2001 From: David Kaya Date: Fri, 27 Mar 2026 21:43:21 +0100 Subject: [PATCH] fix: auto-resolve queued approvals of the same category When multiple tool calls of the same permission category (e.g. three concurrent view/read calls) arrive simultaneously, approving one now cascades to all queued approvals sharing the same category key. This eliminates repeated approval prompts for parallel tool calls. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/main/AryxAppService.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/AryxAppService.ts b/src/main/AryxAppService.ts index 4822f0c..494c1a8 100644 --- a/src/main/AryxAppService.ts +++ b/src/main/AryxAppService.ts @@ -741,6 +741,27 @@ export class AryxAppService extends EventEmitter { const updatedRun = this.updateSessionRun(session, handle.requestId, (run) => upsertRunApprovalEvent(run, resolvedApproval)); + // Auto-resolve queued approvals that share the same category key. + // When the user approves "read", all pending view/grep/glob calls resolve too. + const cascadeHandles: PendingApprovalHandle[] = []; + if (decision === 'approved' && approvalKey && approval.kind === 'tool-call') { + for (const queued of listPendingApprovals(session)) { + if (queued.id === approvalId) continue; + const queuedKey = resolveApprovalToolKey(queued.toolName, queued.permissionKind); + if (queuedKey !== approvalKey) continue; + + const queuedHandle = this.pendingApprovalHandles.get(queued.id); + if (!queuedHandle || queuedHandle.sessionId !== sessionId) continue; + + const cascadeResolved = resolvePendingApproval(queued, 'approved', resolvedAt); + this.setSessionPendingApprovalState(session, dequeuePendingApprovalState(session, queued.id)); + this.updateSessionRun(session, queuedHandle.requestId, (run) => + upsertRunApprovalEvent(run, cascadeResolved)); + this.pendingApprovalHandles.delete(queued.id); + cascadeHandles.push(queuedHandle); + } + } + const result = await this.persistAndBroadcast(workspace); if (updatedRun) { this.emitRunUpdated(sessionId, resolvedAt, updatedRun); @@ -750,6 +771,9 @@ export class AryxAppService extends EventEmitter { try { await Promise.resolve(handle.resolve(decision, alwaysApprove)); + for (const cascaded of cascadeHandles) { + await Promise.resolve(cascaded.resolve('approved', alwaysApprove)); + } } catch (error) { const failedAt = nowIso(); this.rejectPendingApprovals(