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/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 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") }