Skip to content

Commit dfa0281

Browse files
committed
fix(app): auto-accept permissions
1 parent 4a94096 commit dfa0281

4 files changed

Lines changed: 74 additions & 45 deletions

File tree

packages/app/src/components/prompt-input.tsx

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,43 +1310,45 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
13101310
</div>
13111311
</div>
13121312

1313-
<Show when={store.mode === "normal" && permission.permissionsEnabled() && params.id}>
1314-
<div class="pointer-events-none absolute bottom-2 left-2">
1315-
<div class="pointer-events-auto">
1316-
<TooltipKeybind
1317-
placement="top"
1318-
gutter={8}
1319-
title={language.t(
1320-
accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable",
1321-
)}
1322-
keybind={command.keybind("permissions.autoaccept")}
1313+
<div class="pointer-events-none absolute bottom-2 left-2">
1314+
<div class="pointer-events-auto">
1315+
<TooltipKeybind
1316+
placement="top"
1317+
gutter={8}
1318+
title={language.t(
1319+
accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable",
1320+
)}
1321+
keybind={command.keybind("permissions.autoaccept")}
1322+
>
1323+
<Button
1324+
data-action="prompt-permissions"
1325+
variant="ghost"
1326+
disabled={!params.id}
1327+
onClick={() => {
1328+
if (!params.id) return
1329+
permission.toggleAutoAccept(params.id, sdk.directory)
1330+
}}
1331+
classList={{
1332+
"size-6 flex items-center justify-center": true,
1333+
"text-text-base": !accepting(),
1334+
"hover:bg-surface-success-base": accepting(),
1335+
}}
1336+
aria-label={
1337+
accepting()
1338+
? language.t("command.permissions.autoaccept.disable")
1339+
: language.t("command.permissions.autoaccept.enable")
1340+
}
1341+
aria-pressed={accepting()}
13231342
>
1324-
<Button
1325-
data-action="prompt-permissions"
1326-
variant="ghost"
1327-
onClick={() => permission.toggleAutoAccept(params.id!, sdk.directory)}
1328-
classList={{
1329-
"_hidden group-hover/prompt-input:flex size-6 items-center justify-center": true,
1330-
"text-text-base": !accepting(),
1331-
"hover:bg-surface-success-base": accepting(),
1332-
}}
1333-
aria-label={
1334-
accepting()
1335-
? language.t("command.permissions.autoaccept.disable")
1336-
: language.t("command.permissions.autoaccept.enable")
1337-
}
1338-
aria-pressed={accepting()}
1339-
>
1340-
<Icon
1341-
name="chevron-double-right"
1342-
size="small"
1343-
classList={{ "text-icon-success-base": accepting() }}
1344-
/>
1345-
</Button>
1346-
</TooltipKeybind>
1347-
</div>
1343+
<Icon
1344+
name="chevron-double-right"
1345+
size="small"
1346+
classList={{ "text-icon-success-base": accepting() }}
1347+
/>
1348+
</Button>
1349+
</TooltipKeybind>
13481350
</div>
1349-
</Show>
1351+
</div>
13501352
</div>
13511353
</DockShellForm>
13521354
<Show when={store.mode === "normal" || store.mode === "shell"}>

packages/app/src/context/permission-auto-respond.test.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,33 @@ describe("autoRespondsPermission", () => {
3131
expect(autoRespondsPermission({ root: true }, sessions, permission("child"), "/tmp/project")).toBe(true)
3232
})
3333

34-
test("ignores auto-accept from unrelated sessions", () => {
34+
test("defaults to auto-accept when no lineage override exists", () => {
3535
const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" }), session({ id: "other" })]
3636
const autoAccept = {
3737
other: true,
3838
}
3939

40-
expect(autoRespondsPermission(autoAccept, sessions, permission("child"), "/tmp/project")).toBe(false)
40+
expect(autoRespondsPermission(autoAccept, sessions, permission("child"), "/tmp/project")).toBe(true)
41+
})
42+
43+
test("inherits a parent session's false override", () => {
44+
const directory = "/tmp/project"
45+
const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" })]
46+
const autoAccept = {
47+
[`${base64Encode(directory)}/root`]: false,
48+
}
49+
50+
expect(autoRespondsPermission(autoAccept, sessions, permission("child"), directory)).toBe(false)
51+
})
52+
53+
test("prefers a child override over parent override", () => {
54+
const directory = "/tmp/project"
55+
const sessions = [session({ id: "root" }), session({ id: "child", parentID: "root" })]
56+
const autoAccept = {
57+
[`${base64Encode(directory)}/root`]: false,
58+
[`${base64Encode(directory)}/child`]: true,
59+
}
60+
61+
expect(autoRespondsPermission(autoAccept, sessions, permission("child"), directory)).toBe(true)
4162
})
4263
})

packages/app/src/context/permission-auto-respond.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ export function acceptKey(sessionID: string, directory?: string) {
55
return `${base64Encode(directory)}/${sessionID}`
66
}
77

8+
function accepted(autoAccept: Record<string, boolean>, sessionID: string, directory?: string) {
9+
const key = acceptKey(sessionID, directory)
10+
return autoAccept[key] ?? autoAccept[sessionID]
11+
}
12+
813
function sessionLineage(session: { id: string; parentID?: string }[], sessionID: string) {
914
const parent = session.reduce((acc, item) => {
1015
if (item.parentID) acc.set(item.id, item.parentID)
@@ -29,8 +34,8 @@ export function autoRespondsPermission(
2934
permission: { sessionID: string },
3035
directory?: string,
3136
) {
32-
return sessionLineage(session, permission.sessionID).some((id) => {
33-
const key = acceptKey(id, directory)
34-
return autoAccept[key] ?? autoAccept[id] ?? false
35-
})
37+
const value = sessionLineage(session, permission.sessionID)
38+
.map((id) => accepted(autoAccept, id, directory))
39+
.find((item): item is boolean => item !== undefined)
40+
return value ?? true
3641
}

packages/app/src/context/permission.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
115115
}
116116

117117
function isAutoAccepting(sessionID: string, directory?: string) {
118-
const key = acceptKey(sessionID, directory)
119-
return store.autoAccept[key] ?? store.autoAccept[sessionID] ?? false
118+
const session = directory ? globalSync.child(directory, { bootstrap: false })[0].session : []
119+
return autoRespondsPermission(store.autoAccept, session, { sessionID }, directory)
120120
}
121121

122122
function shouldAutoRespond(permission: PermissionRequest, directory?: string) {
@@ -168,10 +168,11 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
168168

169169
function disable(sessionID: string, directory?: string) {
170170
bumpEnableVersion(sessionID, directory)
171-
const key = directory ? acceptKey(sessionID, directory) : undefined
171+
const key = directory ? acceptKey(sessionID, directory) : sessionID
172172
setStore(
173173
produce((draft) => {
174-
if (key) delete draft.autoAccept[key]
174+
draft.autoAccept[key] = false
175+
if (!directory) return
175176
delete draft.autoAccept[sessionID]
176177
}),
177178
)

0 commit comments

Comments
 (0)