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>
This commit is contained in:
David Kaya
2026-03-27 21:43:21 +01:00
co-authored by Copilot
parent 689b335220
commit b5ab92e444
+24
View File
@@ -741,6 +741,27 @@ export class AryxAppService extends EventEmitter<AppServiceEvents> {
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<AppServiceEvents> {
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(