summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/context
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-26 20:36:10 -0600
committerAdam <[email protected]>2026-02-26 20:36:10 -0600
commite9a7c7114184d0092c114ce7a7d9446cf0d366cc (patch)
tree9255b5a23422d4b5ecdc9df77da17993aaab7cfd /packages/app/src/context
parent4205fbd2aa98c6f62c8caae94e909a6048afbf53 (diff)
downloadopencode-e9a7c7114184d0092c114ce7a7d9446cf0d366cc.tar.gz
opencode-e9a7c7114184d0092c114ce7a7d9446cf0d366cc.zip
fix(app): permission notifications
Diffstat (limited to 'packages/app/src/context')
-rw-r--r--packages/app/src/context/permission-auto-respond.test.ts42
-rw-r--r--packages/app/src/context/permission-auto-respond.ts36
-rw-r--r--packages/app/src/context/permission.tsx18
3 files changed, 87 insertions, 9 deletions
diff --git a/packages/app/src/context/permission-auto-respond.test.ts b/packages/app/src/context/permission-auto-respond.test.ts
new file mode 100644
index 000000000..1fa1ff3de
--- /dev/null
+++ b/packages/app/src/context/permission-auto-respond.test.ts
@@ -0,0 +1,42 @@
+import { describe, expect, test } from "bun:test"
+import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client"
+import { base64Encode } from "@opencode-ai/util/encode"
+import { autoRespondsPermission } from "./permission-auto-respond"
+
+const session = (input: { id: string; parentID?: string }) =>
+ ({
+ id: input.id,
+ parentID: input.parentID,
+ }) as Session
+
+const permission = (sessionID: string) =>
+ ({
+ sessionID,
+ }) as Pick<PermissionRequest, "sessionID">
+
+describe("autoRespondsPermission", () => {
+ test("uses a parent session's directory-scoped auto-accept", () => {
+ const directory = "/tmp/project"
+ const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" })]
+ const autoAccept = {
+ [`${base64Encode(directory)}/root`]: true,
+ }
+
+ expect(autoRespondsPermission(autoAccept, sessions, permission("child"), directory)).toBe(true)
+ })
+
+ test("uses a parent session's legacy auto-accept key", () => {
+ const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" })]
+
+ expect(autoRespondsPermission({ root: true }, sessions, permission("child"), "/tmp/project")).toBe(true)
+ })
+
+ test("ignores auto-accept from unrelated sessions", () => {
+ const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" }), session({ id: "other" })]
+ const autoAccept = {
+ other: true,
+ }
+
+ expect(autoRespondsPermission(autoAccept, sessions, permission("child"), "/tmp/project")).toBe(false)
+ })
+})
diff --git a/packages/app/src/context/permission-auto-respond.ts b/packages/app/src/context/permission-auto-respond.ts
new file mode 100644
index 000000000..e45e5f51c
--- /dev/null
+++ b/packages/app/src/context/permission-auto-respond.ts
@@ -0,0 +1,36 @@
+import { base64Encode } from "@opencode-ai/util/encode"
+
+export function acceptKey(sessionID: string, directory?: string) {
+ if (!directory) return sessionID
+ return `${base64Encode(directory)}/${sessionID}`
+}
+
+function sessionLineage(session: { id: string; parentID?: string }[], sessionID: string) {
+ const parent = session.reduce((acc, item) => {
+ if (item.parentID) acc.set(item.id, item.parentID)
+ return acc
+ }, new Map<string, string>())
+ const seen = new Set([sessionID])
+ const ids = [sessionID]
+
+ for (const id of ids) {
+ const parentID = parent.get(id)
+ if (!parentID || seen.has(parentID)) continue
+ seen.add(parentID)
+ ids.push(parentID)
+ }
+
+ return ids
+}
+
+export function autoRespondsPermission(
+ autoAccept: Record<string, boolean>,
+ session: { id: string; parentID?: string }[],
+ permission: { sessionID: string },
+ directory?: string,
+) {
+ return sessionLineage(session, permission.sessionID).some((id) => {
+ const key = acceptKey(id, directory)
+ return autoAccept[key] ?? autoAccept[id] ?? false
+ })
+}
diff --git a/packages/app/src/context/permission.tsx b/packages/app/src/context/permission.tsx
index ccfda5e69..d63d4d568 100644
--- a/packages/app/src/context/permission.tsx
+++ b/packages/app/src/context/permission.tsx
@@ -6,8 +6,8 @@ import { Persist, persisted } from "@/utils/persist"
import { useGlobalSDK } from "@/context/global-sdk"
import { useGlobalSync } from "./global-sync"
import { useParams } from "@solidjs/router"
-import { base64Encode } from "@opencode-ai/util/encode"
import { decode64 } from "@/utils/base64"
+import { acceptKey, autoRespondsPermission } from "./permission-auto-respond"
type PermissionRespondFn = (input: {
sessionID: string
@@ -114,16 +114,16 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
})
}
- function acceptKey(sessionID: string, directory?: string) {
- if (!directory) return sessionID
- return `${base64Encode(directory)}/${sessionID}`
- }
-
function isAutoAccepting(sessionID: string, directory?: string) {
const key = acceptKey(sessionID, directory)
return store.autoAccept[key] ?? store.autoAccept[sessionID] ?? false
}
+ function shouldAutoRespond(permission: PermissionRequest, directory?: string) {
+ const session = directory ? globalSync.child(directory, { bootstrap: false })[0].session : []
+ return autoRespondsPermission(store.autoAccept, session, permission, directory)
+ }
+
function bumpEnableVersion(sessionID: string, directory?: string) {
const key = acceptKey(sessionID, directory)
const next = (enableVersion.get(key) ?? 0) + 1
@@ -136,7 +136,7 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
if (event?.type !== "permission.asked") return
const perm = event.properties
- if (!isAutoAccepting(perm.sessionID, e.name)) return
+ if (!shouldAutoRespond(perm, e.name)) return
respondOnce(perm, e.name)
})
@@ -159,7 +159,7 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
if (!isAutoAccepting(sessionID, directory)) return
for (const perm of x.data ?? []) {
if (!perm?.id) continue
- if (perm.sessionID !== sessionID) continue
+ if (!shouldAutoRespond(perm, directory)) continue
respondOnce(perm, directory)
}
})
@@ -181,7 +181,7 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
ready,
respond,
autoResponds(permission: PermissionRequest, directory?: string) {
- return isAutoAccepting(permission.sessionID, directory)
+ return shouldAutoRespond(permission, directory)
},
isAutoAccepting,
toggleAutoAccept(sessionID: string, directory: string) {