Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/AgentChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const AgentChat: React.FC = () => {
}
})()}

<UserInput />
{!state.pendingToolPermission && <UserInput />}

<Box marginTop={1} />
</Box>
Expand Down
15 changes: 2 additions & 13 deletions src/components/ToolPermissionPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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("")
Expand Down
25 changes: 25 additions & 0 deletions src/utils/MessageQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { EventEmitter } from "events"

export class MessageQueue extends EventEmitter {
private messageBuffer: string[] = []
private permissionBuffer: string[] = []

constructor() {
super()
Expand All @@ -25,15 +26,39 @@ export class MessageQueue extends EventEmitter {
}
}

async waitForPermissionResponse(): Promise<string> {
if (this.permissionBuffer.length > 0) {
return this.permissionBuffer.shift()!
}

return new Promise<string>((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 () => {
Expand Down
26 changes: 13 additions & 13 deletions src/utils/__tests__/canUseTool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("yes")
queue.sendPermissionResponse("yes")
const result = await promise

expect(result.behavior).toBe("allow")
Expand All @@ -37,7 +37,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("y")
queue.sendPermissionResponse("y")
const result = await promise

expect(result.behavior).toBe("allow")
Expand All @@ -55,7 +55,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("allow")
queue.sendPermissionResponse("allow")
const result = await promise

expect(result.behavior).toBe("allow")
Expand All @@ -73,7 +73,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("no")
queue.sendPermissionResponse("no")
const result = await promise

expect(result.behavior).toBe("deny")
Expand All @@ -95,7 +95,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("n")
queue.sendPermissionResponse("n")
const result = await promise

expect(result.behavior).toBe("deny")
Expand All @@ -113,7 +113,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("deny")
queue.sendPermissionResponse("deny")
const result = await promise

expect(result.behavior).toBe("deny")
Expand All @@ -131,7 +131,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("YES")
queue.sendPermissionResponse("YES")
const result = await promise

expect(result.behavior).toBe("allow")
Expand All @@ -149,7 +149,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage(" yes ")
queue.sendPermissionResponse(" yes ")
const result = await promise

expect(result.behavior).toBe("allow")
Expand All @@ -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")
Expand All @@ -193,7 +193,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("yes")
queue.sendPermissionResponse("yes")
await promise

expect(callback).toHaveBeenCalledTimes(1)
Expand All @@ -212,7 +212,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("yes")
queue.sendPermissionResponse("yes")
const result = await promise

expect(result.behavior).toBe("allow")
Expand Down Expand Up @@ -250,7 +250,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("yes")
queue.sendPermissionResponse("yes")
const result = await promise

expect(result.behavior).toBe("allow")
Expand All @@ -275,7 +275,7 @@ describe("createCanUseTool", () => {
}
)

queue.sendMessage("no")
queue.sendPermissionResponse("no")
await promise
await new Promise((resolve) => setTimeout(resolve, 100))

Expand Down
2 changes: 1 addition & 1 deletion src/utils/canUseTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down