Merge pull request #421 from Githubguy132010/fix/vscode-permission-prompt-408

fix(vscode): stop duplicate permission prompts (#408)
This commit is contained in:
Remon Oldenbeuving
2026-02-20 12:11:33 +01:00
committed by GitHub
5 changed files with 81 additions and 7 deletions
@@ -0,0 +1,53 @@
import { describe, expect, it } from "bun:test"
import { removeSessionPermissions, upsertPermission } from "../../webview-ui/src/context/permission-queue"
import type { PermissionRequest } from "../../webview-ui/src/types/messages"
const permission = (input: Partial<PermissionRequest> = {}): PermissionRequest => ({
id: input.id ?? "perm-1",
sessionID: input.sessionID ?? "session-1",
toolName: input.toolName ?? "read",
patterns: input.patterns ?? ["/tmp/*"],
args: input.args ?? {},
message: input.message,
tool: input.tool,
})
describe("permission queue", () => {
it("appends a new permission id", () => {
const result = upsertPermission([], permission({ id: "perm-1" }))
expect(result).toHaveLength(1)
expect(result[0].id).toBe("perm-1")
})
it("updates an existing permission id instead of duplicating", () => {
const existing = permission({ id: "perm-1", toolName: "read", patterns: ["a"] })
const incoming = permission({ id: "perm-1", toolName: "write", patterns: ["b"] })
const result = upsertPermission([existing], incoming)
expect(result).toHaveLength(1)
expect(result[0]).toEqual(incoming)
})
it("keeps other permission entries when updating one id", () => {
const first = permission({ id: "perm-1", sessionID: "session-1" })
const second = permission({ id: "perm-2", sessionID: "session-2" })
const incoming = permission({ id: "perm-1", toolName: "edit" })
const result = upsertPermission([first, second], incoming)
expect(result).toHaveLength(2)
expect(result.find((item) => item.id === "perm-1")).toEqual(incoming)
expect(result.find((item) => item.id === "perm-2")).toEqual(second)
})
it("removes only permissions from the deleted session", () => {
const first = permission({ id: "perm-1", sessionID: "session-1" })
const second = permission({ id: "perm-2", sessionID: "session-2" })
const third = permission({ id: "perm-3", sessionID: "session-1" })
const result = removeSessionPermissions([first, second, third], "session-1")
expect(result).toEqual([second])
})
})
+11 -5
View File
@@ -50,15 +50,21 @@ export const DataBridge: Component<{ children: any }> = (props) => {
const data = createMemo(() => {
const id = session.currentSessionID()
const msgs = session.allMessages()
const parts = session.allParts()
const perms = id ? session.permissions().filter((p) => p.sessionID === id) : []
return {
session: session.sessions().map((s) => ({ ...s, id: s.id, role: "user" as const })) as unknown as any[],
session_status: {} as Record<string, any>,
session_diff: {} as Record<string, any[]>,
message: msgs as unknown as Record<string, SDKMessage[]>,
part: parts as unknown as Record<string, SDKPart[]>,
permission: id ? { [id]: session.permissions() as unknown as any[] } : {},
message: id ? { [id]: session.messages() as unknown as SDKMessage[] } : {},
part: id
? Object.fromEntries(
session
.messages()
.map((msg) => [msg.id, session.getParts(msg.id) as unknown as SDKPart[]])
.filter(([, parts]) => (parts as SDKPart[]).length > 0),
)
: {},
permission: id ? { [id]: perms as unknown as any[] } : {},
}
})
@@ -27,7 +27,7 @@ export const ChatView: Component<ChatViewProps> = (props) => {
const sessionPermissions = () => session.permissions().filter((p) => p.sessionID === id())
const questionRequest = () => sessionQuestions()[0]
const permissionRequest = () => sessionPermissions()[0]
const permissionRequest = () => sessionPermissions().find((p) => !p.tool)
const blocked = () => sessionPermissions().length > 0 || sessionQuestions().length > 0
const [responding, setResponding] = createSignal(false)
@@ -0,0 +1,13 @@
import type { PermissionRequest } from "../types/messages"
export function upsertPermission(list: PermissionRequest[], permission: PermissionRequest) {
const idx = list.findIndex((item) => item.id === permission.id)
if (idx === -1) return [...list, permission]
const next = list.slice()
next[idx] = permission
return next
}
export function removeSessionPermissions(list: PermissionRequest[], sessionID: string) {
return list.filter((item) => item.sessionID !== sessionID)
}
@@ -37,6 +37,7 @@ import type {
ExtensionMessage,
FileAttachment,
} from "../types/messages"
import { removeSessionPermissions, upsertPermission } from "./permission-queue"
// Derive human-readable status from the last streaming part
function computeStatus(
@@ -508,7 +509,7 @@ export const SessionProvider: ParentComponent = (props) => {
}
function handlePermissionRequest(permission: PermissionRequest) {
setPermissions((prev) => [...prev, permission])
setPermissions((prev) => upsertPermission(prev, permission))
}
function handleQuestionRequest(question: QuestionRequest) {
@@ -603,6 +604,7 @@ export const SessionProvider: ParentComponent = (props) => {
return next
})
}
setPermissions((prev) => removeSessionPermissions(prev, sessionID))
setStatusMap(
produce((map) => {
delete map[sessionID]