Apply PR #12633: feat(tui): add auto-accept mode for permission requests

This commit is contained in:
opencode-agent[bot] 2026-04-21 01:00:43 +00:00
commit d30eb3e89e
7 changed files with 41 additions and 6 deletions

View file

@ -94,6 +94,7 @@ export const layer = Layer.effect(
question: "deny", question: "deny",
plan_enter: "deny", plan_enter: "deny",
plan_exit: "deny", plan_exit: "deny",
edit: "ask",
// mirrors github.com/github/gitignore Node.gitignore pattern for .env files // mirrors github.com/github/gitignore Node.gitignore pattern for .env files
read: { read: {
"*": "allow", "*": "allow",

View file

@ -367,6 +367,11 @@ export const RunCommand = cmd({
action: "deny", action: "deny",
pattern: "*", pattern: "*",
}, },
{
permission: "edit",
action: "allow",
pattern: "*",
},
] ]
function title() { function title() {

View file

@ -112,6 +112,7 @@ export function Prompt(props: PromptProps) {
const [auto, setAuto] = createSignal<AutocompleteRef>() const [auto, setAuto] = createSignal<AutocompleteRef>()
const currentProviderLabel = createMemo(() => local.model.parsed().provider) const currentProviderLabel = createMemo(() => local.model.parsed().provider)
const hasRightContent = createMemo(() => Boolean(props.right)) const hasRightContent = createMemo(() => Boolean(props.right))
const [autoaccept, setAutoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
function promptModelWarning() { function promptModelWarning() {
toast.show({ toast.show({
@ -228,6 +229,17 @@ export function Prompt(props: PromptProps) {
command.register(() => { command.register(() => {
return [ return [
{
title: autoaccept() === "none" ? "Enable autoedit" : "Disable autoedit",
value: "permission.auto_accept.toggle",
search: "toggle permissions",
keybind: "permission_auto_accept_toggle",
category: "Agent",
onSelect: (dialog) => {
setAutoaccept(() => (autoaccept() === "none" ? "edit" : "none"))
dialog.clear()
},
},
{ {
title: "Clear prompt", title: "Clear prompt",
value: "prompt.clear", value: "prompt.clear",
@ -1221,8 +1233,13 @@ export function Prompt(props: PromptProps) {
)} )}
</Show> </Show>
</box> </box>
<Show when={hasRightContent()}> <Show when={hasRightContent() || autoaccept() === "edit"}>
<box flexDirection="row" gap={1} alignItems="center"> <box flexDirection="row" gap={1} alignItems="center">
<Show when={autoaccept() === "edit"}>
<text>
<span style={{ fg: theme.warning }}>autoedit</span>
</text>
</Show>
{props.right} {props.right}
</box> </box>
</Show> </Show>

View file

@ -27,6 +27,7 @@ import { createSimpleContext } from "./helper"
import type { Snapshot } from "@/snapshot" import type { Snapshot } from "@/snapshot"
import { useExit } from "./exit" import { useExit } from "./exit"
import { useArgs } from "./args" import { useArgs } from "./args"
import { useKV } from "./kv"
import { batch, onMount } from "solid-js" import { batch, onMount } from "solid-js"
import { Log } from "@/util" import { Log } from "@/util"
import { emptyConsoleState, type ConsoleState } from "@/config/console-state" import { emptyConsoleState, type ConsoleState } from "@/config/console-state"
@ -107,6 +108,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
const event = useEvent() const event = useEvent()
const project = useProject() const project = useProject()
const sdk = useSDK() const sdk = useSDK()
const kv = useKV()
const [autoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
const fullSyncedSessions = new Set<string>() const fullSyncedSessions = new Set<string>()
let syncedWorkspace = project.workspace.current() let syncedWorkspace = project.workspace.current()
@ -133,6 +136,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
case "permission.asked": { case "permission.asked": {
const request = event.properties const request = event.properties
if (autoaccept() === "edit" && request.permission === "edit") {
sdk.client.permission.reply({
reply: "once",
requestID: request.id,
})
break
}
const requests = store.permission[request.sessionID] const requests = store.permission[request.sessionID]
if (!requests) { if (!requests) {
setStore("permission", request.sessionID, [request]) setStore("permission", request.sessionID, [request])

View file

@ -37,6 +37,7 @@ export interface DialogSelectOption<T = any> {
title: string title: string
value: T value: T
description?: string description?: string
search?: string
footer?: JSX.Element | string footer?: JSX.Element | string
category?: string category?: string
categoryView?: JSX.Element categoryView?: JSX.Element
@ -93,8 +94,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
// users typically search by the item name, and not its category. // users typically search by the item name, and not its category.
const result = fuzzysort const result = fuzzysort
.go(needle, options, { .go(needle, options, {
keys: ["title", "category"], keys: ["title", "category", "search"],
scoreFn: (r) => r[0].score * 2 + r[1].score, scoreFn: (r) => r[0].score * 2 + r[1].score + r[2].score,
}) })
.map((x) => x.obj) .map((x) => x.obj)

View file

@ -62,6 +62,7 @@ const KeybindsSchema = Schema.Struct({
agent_list: keybind("<leader>a", "List agents"), agent_list: keybind("<leader>a", "List agents"),
agent_cycle: keybind("tab", "Next agent"), agent_cycle: keybind("tab", "Next agent"),
agent_cycle_reverse: keybind("shift+tab", "Previous agent"), agent_cycle_reverse: keybind("shift+tab", "Previous agent"),
permission_auto_accept_toggle: keybind("none", "Toggle auto-accept mode for permissions"),
variant_cycle: keybind("ctrl+t", "Cycle model variants"), variant_cycle: keybind("ctrl+t", "Cycle model variants"),
variant_list: keybind("none", "List model variants"), variant_list: keybind("none", "List model variants"),
input_clear: keybind("ctrl+c", "Clear input field"), input_clear: keybind("ctrl+c", "Clear input field"),

View file

@ -47,7 +47,7 @@ test("build agent has correct default properties", async () => {
expect(build).toBeDefined() expect(build).toBeDefined()
expect(build?.mode).toBe("primary") expect(build?.mode).toBe("primary")
expect(build?.native).toBe(true) expect(build?.native).toBe(true)
expect(evalPerm(build, "edit")).toBe("allow") expect(evalPerm(build, "edit")).toBe("ask")
expect(evalPerm(build, "bash")).toBe("allow") expect(evalPerm(build, "bash")).toBe("allow")
}, },
}) })
@ -224,8 +224,8 @@ test("agent permission config merges with defaults", async () => {
expect(build).toBeDefined() expect(build).toBeDefined()
// Specific pattern is denied // Specific pattern is denied
expect(Permission.evaluate("bash", "rm -rf *", build!.permission).action).toBe("deny") expect(Permission.evaluate("bash", "rm -rf *", build!.permission).action).toBe("deny")
// Edit still allowed // Edit still asks (default behavior)
expect(evalPerm(build, "edit")).toBe("allow") expect(evalPerm(build, "edit")).toBe("ask")
}, },
}) })
}) })