diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 0709fb253e..b3a85cc932 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -166,13 +166,19 @@ export function Prompt(props: PromptProps) { if (!props.disabled) input.cursorColor = theme.text }) + const lastUserMessage = createMemo(() => { + if (!props.sessionID) return undefined + const messages = sync.data.message[props.sessionID] + if (!messages) return undefined + return messages.findLast((m) => m.role === "user") + }) + const [store, setStore] = createStore<{ prompt: PromptInfo mode: "normal" | "shell" extmarkToPartIndex: Map interrupt: number placeholder: number - effort: MessageV2.Thinking["effort"] }>({ placeholder: Math.floor(Math.random() * PLACEHOLDERS.length), prompt: { @@ -182,7 +188,27 @@ export function Prompt(props: PromptProps) { mode: "normal", extmarkToPartIndex: new Map(), interrupt: 0, - effort: "default", + }) + + // Derive model, agent, and effort from last user message + createEffect(() => { + const msg = lastUserMessage() + if (!msg) return + + // Set agent from last message + if (msg.agent) { + local.agent.set(msg.agent) + } + + // Set model from last message + if (msg.model) { + local.model.set(msg.model) + } + + // Set effort from last message + if (msg.thinking?.effort) { + local.effort.set(msg.thinking.effort) + } }) command.register(() => { @@ -513,7 +539,6 @@ export function Prompt(props: PromptProps) { parts: [], }) setStore("extmarkToPartIndex", new Map()) - setStore("effort", "default") }, submit() { submit() @@ -591,6 +616,8 @@ export function Prompt(props: PromptProps) { arguments: args.join(" "), agent: local.agent.current().name, model: `${selectedModel.providerID}/${selectedModel.modelID}`, + // TODO: allow reasoning to affect command invocations too + // thinking: { effort: store.effort }, messageID, }) } else { @@ -600,6 +627,7 @@ export function Prompt(props: PromptProps) { messageID, agent: local.agent.current().name, model: selectedModel, + thinking: local.effort.current() !== "default" ? { effort: local.effort.current() } : undefined, parts: [ { id: Identifier.ascending("part"), @@ -847,10 +875,7 @@ export function Prompt(props: PromptProps) { } if (keybind.match("effort_cycle", e)) { e.preventDefault() - const levels: MessageV2.Thinking["effort"][] = ["default", "medium", "high"] - const currentIndex = levels.indexOf(store.effort) - const nextIndex = (currentIndex + 1) % levels.length - setStore("effort", levels[nextIndex]) + local.effort.cycle() return } if (store.mode === "normal") autocomplete.onKeyDown(e) @@ -968,10 +993,10 @@ export function Prompt(props: PromptProps) { {local.model.parsed().model} {local.model.parsed().provider} - + ยท - {store.effort} + {local.effort.current()} diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index 55c04621ef..bd6ad56003 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -329,10 +329,34 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ }, } + const effort = iife(() => { + const [effortStore, setEffortStore] = createStore<{ + current: "default" | "medium" | "high" + }>({ + current: "default", + }) + + return { + current() { + return effortStore.current + }, + set(value: "default" | "medium" | "high") { + setEffortStore("current", value) + }, + cycle() { + const levels: Array<"default" | "medium" | "high"> = ["default", "medium", "high"] + const currentIndex = levels.indexOf(effortStore.current) + const nextIndex = (currentIndex + 1) % levels.length + setEffortStore("current", levels[nextIndex]) + }, + } + }) + const result = { model, agent, mcp, + effort, } return result }, diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 97bc92b866..49f71a7423 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -1227,6 +1227,9 @@ export class Session extends HeyApiClient { [key: string]: boolean } system?: string + thinking?: { + effort: "default" | "medium" | "high" + } parts?: Array }, options?: Options, @@ -1244,6 +1247,7 @@ export class Session extends HeyApiClient { { in: "body", key: "noReply" }, { in: "body", key: "tools" }, { in: "body", key: "system" }, + { in: "body", key: "thinking" }, { in: "body", key: "parts" }, ], }, @@ -1313,6 +1317,9 @@ export class Session extends HeyApiClient { [key: string]: boolean } system?: string + thinking?: { + effort: "default" | "medium" | "high" + } parts?: Array }, options?: Options, @@ -1330,6 +1337,7 @@ export class Session extends HeyApiClient { { in: "body", key: "noReply" }, { in: "body", key: "tools" }, { in: "body", key: "system" }, + { in: "body", key: "thinking" }, { in: "body", key: "parts" }, ], }, diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index c8016012f5..a0ad7b8f76 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -2922,6 +2922,9 @@ export type SessionPromptData = { [key: string]: boolean } system?: string + thinking?: { + effort: "default" | "medium" | "high" + } parts: Array } path: { @@ -3105,6 +3108,9 @@ export type SessionPromptAsyncData = { [key: string]: boolean } system?: string + thinking?: { + effort: "default" | "medium" | "high" + } parts: Array } path: {