diff options
| author | Adam <[email protected]> | 2026-03-02 13:10:37 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-03-02 13:10:37 -0600 |
| commit | 78069369e2253c9788c09b7a71478d140c9741f2 (patch) | |
| tree | e15011250cd1a92953dfe0584facbc26bcb92309 | |
| parent | 1cd77b10724ad87acc96790bec4a524614b20ad6 (diff) | |
| download | opencode-78069369e2253c9788c09b7a71478d140c9741f2.tar.gz opencode-78069369e2253c9788c09b7a71478d140c9741f2.zip | |
fix(app): default auto-respond to false
6 files changed, 77 insertions, 12 deletions
diff --git a/packages/app/e2e/session/session-composer-dock.spec.ts b/packages/app/e2e/session/session-composer-dock.spec.ts index deb87a062..4cf075fc9 100644 --- a/packages/app/e2e/session/session-composer-dock.spec.ts +++ b/packages/app/e2e/session/session-composer-dock.spec.ts @@ -142,6 +142,17 @@ test("default dock shows prompt input", async ({ page, sdk, gotoSession }) => { }) }) +test("auto-accept toggle works before first submit", async ({ page, gotoSession }) => { + await gotoSession() + + const button = page.locator('[data-action="prompt-permissions"]').first() + await expect(button).toBeVisible() + await expect(button).toHaveAttribute("aria-pressed", "false") + + await setAutoAccept(page, true) + await setAutoAccept(page, false) +}) + test("blocked question flow unblocks after submit", async ({ page, sdk, gotoSession }) => { await withDockSession(sdk, "e2e composer dock question", async (session) => { await withDockSeed(sdk, session.id, async () => { diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index b99ac9373..ba5a33a47 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -243,6 +243,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { draggingType: "image" | "@mention" | null mode: "normal" | "shell" applyingHistory: boolean + pendingAutoAccept: boolean }>({ popover: null, historyIndex: -1, @@ -251,6 +252,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { draggingType: null, mode: "normal", applyingHistory: false, + pendingAutoAccept: false, }) const commentCount = createMemo(() => { @@ -301,6 +303,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => { }), ) + createEffect( + on(sessionKey, () => { + setStore("pendingAutoAccept", false) + }), + ) + const historyComments = () => { const byID = new Map(comments.all().map((item) => [`${item.file}\n${item.id}`, item] as const)) return prompt.context.items().flatMap((item) => { @@ -947,10 +955,18 @@ export const PromptInput: Component<PromptInputProps> = (props) => { readClipboardImage: platform.readClipboardImage, }) + const variants = createMemo(() => ["default", ...local.model.variant.list()]) + const accepting = createMemo(() => { + const id = params.id + if (!id) return store.pendingAutoAccept + return permission.isAutoAccepting(id, sdk.directory) + }) + const { abort, handleSubmit } = createPromptSubmit({ info, imageAttachments, commentCount, + autoAccept: () => accepting(), mode: () => store.mode, working, editor: () => editorRef, @@ -1115,13 +1131,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => { } } - const variants = createMemo(() => ["default", ...local.model.variant.list()]) - const accepting = createMemo(() => { - const id = params.id - if (!id) return false - return permission.isAutoAccepting(id, sdk.directory) - }) - return ( <div class="relative size-full _max-h-[320px] flex flex-col gap-0"> <PromptPopover @@ -1313,9 +1322,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <Button data-action="prompt-permissions" variant="ghost" - disabled={!params.id} onClick={() => { - if (!params.id) return + if (!params.id) { + setStore("pendingAutoAccept", (value) => !value) + return + } permission.toggleAutoAccept(params.id, sdk.directory) }} classList={{ diff --git a/packages/app/src/components/prompt-input/submit.test.ts b/packages/app/src/components/prompt-input/submit.test.ts index c3d6a9281..c633525a2 100644 --- a/packages/app/src/components/prompt-input/submit.test.ts +++ b/packages/app/src/components/prompt-input/submit.test.ts @@ -5,6 +5,7 @@ let createPromptSubmit: typeof import("./submit").createPromptSubmit const createdClients: string[] = [] const createdSessions: string[] = [] +const enabledAutoAccept: Array<{ sessionID: string; directory: string }> = [] const sentShell: string[] = [] const syncedDirectories: string[] = [] @@ -69,6 +70,14 @@ beforeAll(async () => { }), })) + mock.module("@/context/permission", () => ({ + usePermission: () => ({ + enableAutoAccept(sessionID: string, directory: string) { + enabledAutoAccept.push({ sessionID, directory }) + }, + }), + })) + mock.module("@/context/prompt", () => ({ usePrompt: () => ({ current: () => promptValue, @@ -145,6 +154,7 @@ beforeAll(async () => { beforeEach(() => { createdClients.length = 0 createdSessions.length = 0 + enabledAutoAccept.length = 0 sentShell.length = 0 syncedDirectories.length = 0 selected = "/repo/worktree-a" @@ -156,6 +166,7 @@ describe("prompt submit worktree selection", () => { info: () => undefined, imageAttachments: () => [], commentCount: () => 0, + autoAccept: () => false, mode: () => "shell", working: () => false, editor: () => undefined, @@ -181,4 +192,31 @@ describe("prompt submit worktree selection", () => { expect(sentShell).toEqual(["/repo/worktree-a", "/repo/worktree-b"]) expect(syncedDirectories).toEqual(["/repo/worktree-a", "/repo/worktree-b"]) }) + + test("applies auto-accept to newly created sessions", async () => { + const submit = createPromptSubmit({ + info: () => undefined, + imageAttachments: () => [], + commentCount: () => 0, + autoAccept: () => true, + mode: () => "shell", + working: () => false, + editor: () => undefined, + queueScroll: () => undefined, + promptLength: (value) => value.reduce((sum, part) => sum + ("content" in part ? part.content.length : 0), 0), + addToHistory: () => undefined, + resetHistoryNavigation: () => undefined, + setMode: () => undefined, + setPopover: () => undefined, + newSessionWorktree: () => selected, + onNewSessionWorktreeReset: () => undefined, + onSubmit: () => undefined, + }) + + const event = { preventDefault: () => undefined } as unknown as Event + + await submit.handleSubmit(event) + + expect(enabledAutoAccept).toEqual([{ sessionID: "session-1", directory: "/repo/worktree-a" }]) + }) }) diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index a7ff39e09..a8c2609f4 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -8,6 +8,7 @@ import { useGlobalSync } from "@/context/global-sync" import { useLanguage } from "@/context/language" import { useLayout } from "@/context/layout" import { useLocal } from "@/context/local" +import { usePermission } from "@/context/permission" import { type ImageAttachmentPart, type Prompt, usePrompt } from "@/context/prompt" import { useSDK } from "@/context/sdk" import { useSync } from "@/context/sync" @@ -27,6 +28,7 @@ type PromptSubmitInput = { info: Accessor<{ id: string } | undefined> imageAttachments: Accessor<ImageAttachmentPart[]> commentCount: Accessor<number> + autoAccept: Accessor<boolean> mode: Accessor<"normal" | "shell"> working: Accessor<boolean> editor: () => HTMLDivElement | undefined @@ -56,6 +58,7 @@ export function createPromptSubmit(input: PromptSubmitInput) { const sync = useSync() const globalSync = useGlobalSync() const local = useLocal() + const permission = usePermission() const prompt = usePrompt() const layout = useLayout() const language = useLanguage() @@ -140,6 +143,7 @@ export function createPromptSubmit(input: PromptSubmitInput) { const projectDirectory = sdk.directory const isNewSession = !params.id + const shouldAutoAccept = isNewSession && input.autoAccept() const worktreeSelection = input.newSessionWorktree?.() || "main" let sessionDirectory = projectDirectory @@ -197,6 +201,7 @@ export function createPromptSubmit(input: PromptSubmitInput) { return undefined }) if (session) { + if (shouldAutoAccept) permission.enableAutoAccept(session.id, sessionDirectory) layout.handoff.setTabs(base64Encode(sessionDirectory), session.id) navigate(`/${base64Encode(sessionDirectory)}/session/${session.id}`) } diff --git a/packages/app/src/context/permission-auto-respond.test.ts b/packages/app/src/context/permission-auto-respond.test.ts index 8657427d7..2e4cf4faf 100644 --- a/packages/app/src/context/permission-auto-respond.test.ts +++ b/packages/app/src/context/permission-auto-respond.test.ts @@ -31,13 +31,13 @@ describe("autoRespondsPermission", () => { expect(autoRespondsPermission({ root: true }, sessions, permission("child"), "/tmp/project")).toBe(true) }) - test("defaults to auto-accept when no lineage override exists", () => { + test("defaults to requiring approval when no lineage override exists", () => { 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(true) + expect(autoRespondsPermission(autoAccept, sessions, permission("child"), "/tmp/project")).toBe(false) }) test("inherits a parent session's false override", () => { diff --git a/packages/app/src/context/permission-auto-respond.ts b/packages/app/src/context/permission-auto-respond.ts index cabd514e7..727ccc937 100644 --- a/packages/app/src/context/permission-auto-respond.ts +++ b/packages/app/src/context/permission-auto-respond.ts @@ -37,5 +37,5 @@ export function autoRespondsPermission( const value = sessionLineage(session, permission.sessionID) .map((id) => accepted(autoAccept, id, directory)) .find((item): item is boolean => item !== undefined) - return value ?? true + return value ?? false } |
