From 362d9eab7a1a64290883da884b295684c1ca7ccf Mon Sep 17 00:00:00 2001 From: Christopher Pappas Date: Sat, 22 Nov 2025 11:47:23 +0100 Subject: [PATCH 1/2] fix: tool permission request handling Refactor tool permission flow to use dedicated permission queue instead of generic message queue. Adds waitForPermissionResponse and sendPermissionResponse methods to handle tool permission confirmations separately, ensuring proper sequencing of permission requests. --- src/components/AgentChat.tsx | 2 +- src/components/ToolPermissionPrompt.tsx | 15 ++------------- src/utils/MessageQueue.ts | 25 +++++++++++++++++++++++++ src/utils/canUseTool.ts | 2 +- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/components/AgentChat.tsx b/src/components/AgentChat.tsx index 67388ed..5bfdaaf 100644 --- a/src/components/AgentChat.tsx +++ b/src/components/AgentChat.tsx @@ -107,7 +107,7 @@ export const AgentChat: React.FC = () => { } })()} - + {!state.pendingToolPermission && } diff --git a/src/components/ToolPermissionPrompt.tsx b/src/components/ToolPermissionPrompt.tsx index 87c1801..323d44d 100644 --- a/src/components/ToolPermissionPrompt.tsx +++ b/src/components/ToolPermissionPrompt.tsx @@ -19,19 +19,8 @@ export const ToolPermissionPrompt: React.FC = () => { const handleSubmit = (value: string) => { const response = value.trim() || "yes" - // Resolve ALL pending tool permission requests with the same response - // This handles the case where multiple tools are called in parallel - while ( - store.messageQueue.hasPendingRequests() && - store.pendingToolPermission - ) { - store.messageQueue.sendMessage(response) - - // Check if there are still more requests after sending - if (!store.messageQueue.hasPendingRequests()) { - break - } - } + // Send the permission response to the waiting canUseTool call + store.messageQueue.sendPermissionResponse(response) actions.setPendingToolPermission(undefined) actions.setInput("") diff --git a/src/utils/MessageQueue.ts b/src/utils/MessageQueue.ts index 9950269..a7338d3 100644 --- a/src/utils/MessageQueue.ts +++ b/src/utils/MessageQueue.ts @@ -2,6 +2,7 @@ import { EventEmitter } from "events" export class MessageQueue extends EventEmitter { private messageBuffer: string[] = [] + private permissionBuffer: string[] = [] constructor() { super() @@ -25,15 +26,39 @@ export class MessageQueue extends EventEmitter { } } + async waitForPermissionResponse(): Promise { + if (this.permissionBuffer.length > 0) { + return this.permissionBuffer.shift()! + } + + return new Promise((resolve) => { + this.once("permissionResponse", resolve) + }) + } + + sendPermissionResponse(value: string): void { + if (this.listenerCount("permissionResponse") > 0) { + this.emit("permissionResponse", value) + } else { + this.permissionBuffer.push(value) + } + } + clear(): void { this.removeAllListeners("message") + this.removeAllListeners("permissionResponse") this.messageBuffer = [] + this.permissionBuffer = [] } hasPendingRequests(): boolean { return this.listenerCount("message") > 0 } + hasPendingPermissionRequest(): boolean { + return this.listenerCount("permissionResponse") > 0 + } + subscribe(callback: (message: string) => void): () => void { this.on("message", callback) return () => { diff --git a/src/utils/canUseTool.ts b/src/utils/canUseTool.ts index 0054d40..6e64461 100644 --- a/src/utils/canUseTool.ts +++ b/src/utils/canUseTool.ts @@ -25,7 +25,7 @@ export const createCanUseTool = (options: CanUseToolOptions) => { onToolPermissionRequest(toolName, input) } - const userResponse = await messageQueue.waitForMessage() + const userResponse = await messageQueue.waitForPermissionResponse() const response = userResponse.toLowerCase().trim() const CONFIRM = ["y", "yes", "allow"].includes(response) From 8d759dab1b76f9545eebbe691dc1289d1ac6c0c8 Mon Sep 17 00:00:00 2001 From: Christopher Pappas Date: Sat, 22 Nov 2025 11:51:07 +0100 Subject: [PATCH 2/2] test: update tests --- src/utils/__tests__/canUseTool.test.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/utils/__tests__/canUseTool.test.ts b/src/utils/__tests__/canUseTool.test.ts index 9f5f4b7..73a0c56 100644 --- a/src/utils/__tests__/canUseTool.test.ts +++ b/src/utils/__tests__/canUseTool.test.ts @@ -16,7 +16,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("yes") + queue.sendPermissionResponse("yes") const result = await promise expect(result.behavior).toBe("allow") @@ -37,7 +37,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("y") + queue.sendPermissionResponse("y") const result = await promise expect(result.behavior).toBe("allow") @@ -55,7 +55,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("allow") + queue.sendPermissionResponse("allow") const result = await promise expect(result.behavior).toBe("allow") @@ -73,7 +73,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("no") + queue.sendPermissionResponse("no") const result = await promise expect(result.behavior).toBe("deny") @@ -95,7 +95,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("n") + queue.sendPermissionResponse("n") const result = await promise expect(result.behavior).toBe("deny") @@ -113,7 +113,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("deny") + queue.sendPermissionResponse("deny") const result = await promise expect(result.behavior).toBe("deny") @@ -131,7 +131,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("YES") + queue.sendPermissionResponse("YES") const result = await promise expect(result.behavior).toBe("allow") @@ -149,7 +149,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage(" yes ") + queue.sendPermissionResponse(" yes ") const result = await promise expect(result.behavior).toBe("allow") @@ -167,7 +167,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("do something else") + queue.sendPermissionResponse("do something else") const result = await promise expect(result.behavior).toBe("deny") @@ -193,7 +193,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("yes") + queue.sendPermissionResponse("yes") await promise expect(callback).toHaveBeenCalledTimes(1) @@ -212,7 +212,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("yes") + queue.sendPermissionResponse("yes") const result = await promise expect(result.behavior).toBe("allow") @@ -250,7 +250,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("yes") + queue.sendPermissionResponse("yes") const result = await promise expect(result.behavior).toBe("allow") @@ -275,7 +275,7 @@ describe("createCanUseTool", () => { } ) - queue.sendMessage("no") + queue.sendPermissionResponse("no") await promise await new Promise((resolve) => setTimeout(resolve, 100))