From 160b4b88465754f48be164e64ba11b11070c7d59 Mon Sep 17 00:00:00 2001 From: salema97 Date: Sat, 16 May 2026 04:53:44 -0500 Subject: [PATCH 1/2] feat(yolo): add /yolo toggle command for auto-approving permissions Adds a new /yolo slash command in TUI sessions that toggles auto-approval of all permission requests without prompting the user. Changes: - TUI session commands: new /yolo slash command in command palette - KV store: persists yolo_mode state across sessions - TUI footer: shows 'YOLO' indicator in warning color when active - TUI sidebar: shows 'YOLO MODE' badge above the version footer - TUI sync: auto-replies permissions when kv.get('yolo_mode') is true --- .../opencode/src/cli/cmd/tui/context/sync.tsx | 7 +++++++ .../src/cli/cmd/tui/routes/session/footer.tsx | 5 +++++ .../src/cli/cmd/tui/routes/session/index.tsx | 19 +++++++++++++++++++ .../cli/cmd/tui/routes/session/sidebar.tsx | 9 ++++++++- 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 9f8a384f777f..f0121f5951d4 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -152,6 +152,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ case "permission.asked": { const request = event.properties + if (kv.get("yolo_mode", false)) { + void sdk.client.permission.reply({ + requestID: request.id, + reply: "once", + }) + break + } const requests = store.permission[request.sessionID] if (!requests) { setStore("permission", request.sessionID, [request]) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx index c3a96254e98b..dd54367b6b6d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx @@ -5,11 +5,13 @@ import { useDirectory } from "../../context/directory" import { useConnected } from "../../component/use-connected" import { createStore } from "solid-js/store" import { useRoute } from "../../context/route" +import { useKV } from "../../context/kv" export function Footer() { const { theme } = useTheme() const sync = useSync() const route = useRoute() + const kv = useKV() const mcp = createMemo(() => Object.values(sync.data.mcp).filter((x) => x.status === "connected").length) const mcpError = createMemo(() => Object.values(sync.data.mcp).some((x) => x.status === "failed")) const lsp = createMemo(() => Object.keys(sync.data.lsp)) @@ -82,6 +84,9 @@ export function Footer() { {mcp()} MCP + + YOLO + /status diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index ce651fdbe46c..44b14edff9d8 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -134,6 +134,7 @@ const sessionBindingCommands = [ "session.toggle.actions", "session.toggle.scrollbar", "session.toggle.generic_tool_output", + "session.toggle.yolo", "session.page.up", "session.page.down", "session.line.up", @@ -226,6 +227,7 @@ export function Session() { const [diffWrapMode] = kv.signal<"word" | "none">("diff_wrap_mode", "word") const [_animationsEnabled, _setAnimationsEnabled] = kv.signal("animations_enabled", true) const [showGenericToolOutput, setShowGenericToolOutput] = kv.signal("generic_tool_output_visibility", false) + const [yoloMode, setYoloMode] = kv.signal("yolo_mode", false) const wide = createMemo(() => dimensions().width > 120) const sidebarVisible = createMemo(() => { @@ -730,6 +732,23 @@ export function Session() { dialog.clear() }, }, + { + title: yoloMode() ? "Disable YOLO mode" : "Enable YOLO mode", + value: "session.toggle.yolo", + category: "Session", + slash: { + name: "yolo", + }, + run: () => { + const next = !yoloMode() + setYoloMode(next) + toast.show({ + message: next ? "YOLO mode enabled — permissions will be auto-approved" : "YOLO mode disabled", + variant: next ? "warning" : "success", + }) + dialog.clear() + }, + }, { title: "Page up", value: "session.page.up", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index 0f9214092eba..45aa3812696a 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -5,6 +5,7 @@ import { useTheme } from "../../context/theme" import { useTuiConfig } from "../../context/tui-config" import { InstallationChannel, InstallationVersion } from "@opencode-ai/core/installation/version" import { TuiPluginRuntime } from "@/cli/cmd/tui/plugin/runtime" +import { useKV } from "../../context/kv" import { getScrollAcceleration } from "../../util/scroll" import { WorkspaceLabel } from "../../component/workspace-label" @@ -14,6 +15,7 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) { const sync = useSync() const { theme } = useTheme() const tuiConfig = useTuiConfig() + const kv = useKV() const session = createMemo(() => sync.session.get(props.sessionID)) const workspace = () => { const workspaceID = session()?.workspaceID @@ -85,7 +87,12 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) { - + + + + YOLO MODE + + Open From 8c485568c0e5a72437237e4200a64ef05c1ac5d6 Mon Sep 17 00:00:00 2001 From: salema97 Date: Sat, 16 May 2026 05:41:43 -0500 Subject: [PATCH 2/2] feat(agent): add per-agent thinking toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a field to agent configuration that allows overriding the model's default thinking/reasoning mode per agent. - — forces thinking on (uses model defaults) - — forces thinking off (strips all reasoning options) - — uses model default behavior This is useful for custom agents where you want deterministic, fast responses without reasoning overhead, or conversely where you want to ensure reasoning is enabled regardless of model defaults. Files changed: - config/agent.ts — added to schema and KNOWN_KEYS - agent/agent.ts — added to Info schema and merge logic - session/llm.ts — strips thinking options when agent.thinking=false --- packages/opencode/src/agent/agent.ts | 2 ++ packages/opencode/src/config/agent.ts | 4 ++++ packages/opencode/src/session/llm.ts | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index ce6cf30b6d55..23381bdc1b0b 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -45,6 +45,7 @@ export const Info = Schema.Struct({ prompt: Schema.optional(Schema.String), options: Schema.Record(Schema.String, Schema.Unknown), steps: Schema.optional(Schema.Finite), + thinking: Schema.optional(Schema.Boolean), }).annotate({ identifier: "Agent" }) export type Info = DeepMutable> @@ -302,6 +303,7 @@ export const layer = Layer.effect( item.hidden = value.hidden ?? item.hidden item.name = value.name ?? item.name item.steps = value.steps ?? item.steps + item.thinking = value.thinking ?? item.thinking item.options = mergeDeep(item.options, value.options ?? {}) item.permission = Permission.merge(item.permission, Permission.fromConfig(value.permission ?? {})) } diff --git a/packages/opencode/src/config/agent.ts b/packages/opencode/src/config/agent.ts index a6719e86743a..06839f628b11 100644 --- a/packages/opencode/src/config/agent.ts +++ b/packages/opencode/src/config/agent.ts @@ -45,6 +45,9 @@ const AgentSchema = Schema.StructWithRest( description: "Maximum number of agentic iterations before forcing text-only response", }), maxSteps: Schema.optional(PositiveInt).annotate({ description: "@deprecated Use 'steps' field instead." }), + thinking: Schema.optional(Schema.Boolean).annotate({ + description: "Override thinking/reasoning mode for this agent (true=force on, false=force off, undefined=use model default)", + }), permission: Schema.optional(ConfigPermission.Info), }), [Schema.Record(Schema.String, Schema.Any)], @@ -63,6 +66,7 @@ const KNOWN_KEYS = new Set([ "color", "steps", "maxSteps", + "thinking", "options", "permission", "disable", diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index 0cf3a2398f9b..ff8d65e961e7 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -139,6 +139,24 @@ const live: Layer.Layer< providerOptions: item.options, }) const options = mergeOptions(mergeOptions(mergeOptions(base, input.model.options), input.agent.options), variant) + + // Apply agent-level thinking override + if (input.agent.thinking === false) { + // Strip all thinking/reasoning related options to disable thinking + delete options.thinking + delete options.thinkingConfig + delete options.reasoning + delete options.reasoningEffort + delete options.reasoningConfig + delete options.enable_thinking + delete options.chat_template_args + } else if (input.agent.thinking === true && !input.small) { + // Force-enable thinking by ensuring the model's default thinking options are present + // The base options from ProviderTransform.options() already include thinking defaults + // for reasoning-capable models, so we just need to make sure they weren't stripped + // by agent.options or variant. If the model doesn't support reasoning, this is a no-op. + } + if (isOpenaiOauth) { options.instructions = system.join("\n") }