From 31e76dd11db8eb368216faa175663d8c648df187 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 28 Aug 2026 14:10:15 +0200 Subject: [PATCH 1/2] fix(vscode): honor configured reasoning defaults --- .changeset/fix-agent-reasoning-defaults.md | 5 +++ .../agent-manager-initial-message.test.ts | 18 ++++---- .../tests/unit/session-preferences.test.ts | 14 ++++++- .../tests/unit/session-variant-store.test.ts | 25 +++++++++++ .../tests/unit/session-variants.test.ts | 41 +++++++++++++++++-- .../agent-manager/NewWorktreeDialog.tsx | 19 ++++----- .../agent-manager/initial-message.ts | 2 +- .../src/context/session-preferences.ts | 3 +- .../src/context/session-variant-store.ts | 10 +++-- .../src/context/session-variants.ts | 19 +++++++-- .../webview-ui/src/context/session.tsx | 11 ++--- 11 files changed, 128 insertions(+), 39 deletions(-) create mode 100644 .changeset/fix-agent-reasoning-defaults.md diff --git a/.changeset/fix-agent-reasoning-defaults.md b/.changeset/fix-agent-reasoning-defaults.md new file mode 100644 index 00000000000..ae5b196cc33 --- /dev/null +++ b/.changeset/fix-agent-reasoning-defaults.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Use configured agent reasoning in new sessions instead of outdated saved defaults, and preserve explicit Default selections. diff --git a/packages/kilo-vscode/tests/unit/agent-manager-initial-message.test.ts b/packages/kilo-vscode/tests/unit/agent-manager-initial-message.test.ts index 87714a5cac6..4a80fd35d51 100644 --- a/packages/kilo-vscode/tests/unit/agent-manager-initial-message.test.ts +++ b/packages/kilo-vscode/tests/unit/agent-manager-initial-message.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test" import { initialMessage, initialVariant, seedInitialVariant } from "../../webview-ui/agent-manager/initial-message" describe("Agent Manager initial message", () => { - it("forwards the selected variant to sendMessage", () => { + it.each(["high", ""])("forwards the selected variant %s to sendMessage", (variant) => { const msg = initialMessage({ type: "agentManager.sendInitialMessage", projectId: "project-a", @@ -12,7 +12,7 @@ describe("Agent Manager initial message", () => { providerID: "anthropic", modelID: "claude-sonnet-4", agent: "code", - variant: "high", + variant, }) expect(msg).toEqual({ @@ -23,7 +23,7 @@ describe("Agent Manager initial message", () => { providerID: "anthropic", modelID: "claude-sonnet-4", agent: "code", - variant: "high", + variant, files: undefined, }) }) @@ -38,7 +38,7 @@ describe("Agent Manager initial message", () => { ).toBeUndefined() }) - it("builds the initial session variant state", () => { + it.each(["medium", ""])("builds the initial session variant state for %s", (variant) => { const state = initialVariant( { type: "agentManager.sendInitialMessage", @@ -46,7 +46,7 @@ describe("Agent Manager initial message", () => { worktreeId: "wt-a", providerID: "anthropic", modelID: "claude-sonnet-4", - variant: "medium", + variant, }, "code", ) @@ -56,7 +56,7 @@ describe("Agent Manager initial message", () => { providerID: "anthropic", modelID: "claude-sonnet-4", agent: "code", - value: "medium", + value: variant, }) }) @@ -75,7 +75,7 @@ describe("Agent Manager initial message", () => { ).toBeUndefined() }) - it("seeds initial variant state into the session store", () => { + it.each(["medium", ""])("seeds initial variant %s into the session store", (variant) => { const calls: unknown[] = [] seedInitialVariant( @@ -89,10 +89,10 @@ describe("Agent Manager initial message", () => { worktreeId: "wt-a", providerID: "anthropic", modelID: "claude-sonnet-4", - variant: "medium", + variant, }, ) - expect(calls).toEqual([["session-a", "anthropic", "claude-sonnet-4", "medium", "code"]]) + expect(calls).toEqual([["session-a", "anthropic", "claude-sonnet-4", variant, "code"]]) }) }) diff --git a/packages/kilo-vscode/tests/unit/session-preferences.test.ts b/packages/kilo-vscode/tests/unit/session-preferences.test.ts index 9da16148386..4cdb88e5c3c 100644 --- a/packages/kilo-vscode/tests/unit/session-preferences.test.ts +++ b/packages/kilo-vscode/tests/unit/session-preferences.test.ts @@ -39,6 +39,18 @@ describe("session preference recovery", () => { }) }) + it.each([undefined, ""])("restores model default %s instead of an older effort", (variant) => { + const prefs = resolveMessagePrefs( + [ + msg({ model: { providerID: "anthropic", modelID: "claude-sonnet-4", variant: "high" } }), + msg({ model: { providerID: "anthropic", modelID: "claude-sonnet-4", variant } }), + ], + agents, + ) + + expect(prefs.variant).toBe("") + }) + it("ignores assistant-only model data and invalid agents", () => { const prefs = resolveMessagePrefs( [ @@ -66,7 +78,7 @@ describe("session preference recovery", () => { expect(prefs).toEqual({ agent: "code", model: { providerID: "anthropic", modelID: "claude-sonnet-4" }, - variant: undefined, + variant: "", }) }) }) diff --git a/packages/kilo-vscode/tests/unit/session-variant-store.test.ts b/packages/kilo-vscode/tests/unit/session-variant-store.test.ts index e9410a49d6c..148acc32ced 100644 --- a/packages/kilo-vscode/tests/unit/session-variant-store.test.ts +++ b/packages/kilo-vscode/tests/unit/session-variant-store.test.ts @@ -52,6 +52,31 @@ describe("per-session variant selection", () => { expect(getAgentVariant(store, model, { variants: { low: {}, high: {} } }, "ask")).toBe("high") }) + it("falls back to the configured mode variant", () => { + expect(getAgentVariant({}, model, { variants: { high: {}, max: {} } }, "code", "max")).toBe("max") + }) + + it.each(["anthropic/claude-sonnet-4", variantKey(model, "code")])( + "prefers the configured variant over the remembered preference %s", + (key) => { + const store = { [key]: "high" } + expect(getVariant(store, model, ["high", "max"], "code", "pending-new", "max")).toBe("max") + expect(getAgentVariant(store, model, { variants: { high: {}, max: {} } }, "code", "max")).toBe("max") + }, + ) + + it.each(["low", ""])("preserves a session choice %s above configured and remembered variants", (value) => { + const store = { + [variantKey(model, "code")]: "high", + [variantKey(model, "code", "session-a")]: value, + } + expect(getVariant(store, model, ["low", "high", "max"], "code", "session-a", "max")).toBe(value || undefined) + }) + + it("ignores a configured variant that the model does not support", () => { + expect(getAgentVariant({}, model, { variants: { low: {}, high: {} } }, "code", "max")).toBeUndefined() + }) + it("uses the model default when no variant is selected", () => { expect(getVariant({}, model, variants, "code")).toBeUndefined() expect(getVariant({ [variantKey(model, "code")]: "" }, model, variants, "code")).toBeUndefined() diff --git a/packages/kilo-vscode/tests/unit/session-variants.test.ts b/packages/kilo-vscode/tests/unit/session-variants.test.ts index 474c2e13a7c..f6ccfdc5bd6 100644 --- a/packages/kilo-vscode/tests/unit/session-variants.test.ts +++ b/packages/kilo-vscode/tests/unit/session-variants.test.ts @@ -4,7 +4,8 @@ import type { ExtensionMessage, ModelSelection } from "../../webview-ui/src/type const model: ModelSelection = { providerID: "anthropic", modelID: "claude-sonnet-4" } -function setup(session?: string) { +function setup(session?: string, configured?: string) { + const config = { model: "anthropic/claude-sonnet-4", variant: configured } const selections: Record = {} const messages: Array<{ type: string; key?: string; value?: string }> = [] const order: string[] = [] @@ -17,7 +18,8 @@ function setup(session?: string) { selected: () => model, session: () => session, agent: () => "code", - find: () => ({ variants: { low: {}, high: {} } }), + config: () => config, + find: () => ({ variants: { low: {}, high: {}, max: {} } }), post: (message) => { order.push("post") messages.push(message) @@ -28,7 +30,7 @@ function setup(session?: string) { return () => order.push("unsub") }, }) - return { variants, selections, messages, order, dispatch: (message: ExtensionMessage) => handler?.(message) } + return { variants, config, selections, messages, order, dispatch: (message: ExtensionMessage) => handler?.(message) } } describe("session variants", () => { @@ -51,6 +53,39 @@ describe("session variants", () => { expect(state.selections).toEqual({ "agent/code/anthropic/claude-sonnet-4": "high" }) }) + it("uses the configured agent variant when no picker selection exists", () => { + const state = setup(undefined, "max") + expect(state.variants.agent("code", model)).toBe("max") + expect(state.variants.current()).toBe("max") + expect(state.variants.request()).toBe("max") + }) + + it("uses updated configuration ahead of remembered defaults for new tabs", () => { + const state = setup("pending-new", "high") + state.selections["agent/code/anthropic/claude-sonnet-4"] = "low" + expect(state.variants.current()).toBe("high") + state.config.variant = "max" + expect(state.variants.current()).toBe("max") + expect(state.variants.request()).toBe("max") + expect(state.variants.agent("code", model)).toBe("max") + }) + + it("does not apply a configured variant to another model", () => { + const state = setup("pending-new", "max") + state.config.model = "anthropic/another-model" + expect(state.variants.current()).toBeUndefined() + expect(state.variants.agent("code", model)).toBeUndefined() + }) + + it("sends an explicit model default instead of inheriting the configured agent variant", () => { + const state = setup("session-a", "max") + state.variants.select(undefined) + expect(state.variants.current()).toBeUndefined() + expect(state.variants.request()).toBe("") + expect(state.variants.current("session-b")).toBe("max") + expect(state.variants.request("session-b")).toBe("max") + }) + it("persists global selections but keeps session selections local", () => { const global = setup() global.variants.select("high") diff --git a/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx b/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx index b231e6f22cd..d007e14fc8a 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx @@ -23,7 +23,7 @@ import { useServer } from "../src/context/server" import { useSession } from "../src/context/session" import { useProvider } from "../src/context/provider" import { useConfig } from "../src/context/config" -import { cycleVariant, preserveVariant } from "../src/context/session-variant-store" +import { DEFAULT_VARIANT, cycleVariant, preserveVariant } from "../src/context/session-variant-store" import { ModelSelectorBase } from "../src/components/shared/ModelSelector" import { ModeSwitcherBase } from "../src/components/shared/ModeSwitcher" import { SpeechToTextButton } from "../src/components/speech-to-text/SpeechToTextButton" @@ -184,9 +184,7 @@ export const NewWorktreeDialog: Component<{ const [baseBranchOpen, setBaseBranchOpen] = createSignal(false) const [compareOpen, setCompareOpen] = createSignal(false) const [highlightedIndex, setHighlightedIndex] = createSignal(0) - const [variant, setVariant] = createSignal( - fallback(saved.variant, () => session.variantForAgent(initialAgent, initialModel)), - ) + const [variant, setVariant] = createSignal(saved.variant) const [sandbox, setSandbox] = createSignal(saved.sandbox) const [sandboxDefault, setSandboxDefault] = createSignal() const [sandboxOverride, setSandboxOverride] = createSignal() @@ -211,7 +209,7 @@ export const NewWorktreeDialog: Component<{ setAgent(name) const sel = session.modelForAgent(name) setModel(sel) - setVariant(session.variantForAgent(name, sel)) + setVariant(undefined) } const cycle = (direction: 1 | -1) => { @@ -238,11 +236,10 @@ export const NewWorktreeDialog: Component<{ return Object.keys(found.variants) }) - // Current effective variant — an absent or invalid selection uses the model default. const effectiveVariant = createMemo(() => { const list = variants() if (list.length === 0) return undefined - const stored = variant() + const stored = variant() ?? session.variantForAgent(agent(), model()) return stored && list.includes(stored) ? stored : undefined }) @@ -460,7 +457,7 @@ export const NewWorktreeDialog: Component<{ providerID: sel?.providerID, modelID: sel?.modelID, agent: selectedAgent, - variant: isCompare ? undefined : effectiveVariant(), + variant: isCompare ? undefined : (effectiveVariant() ?? (variants().length > 0 ? DEFAULT_VARIANT : undefined)), baseBranch: effectiveBaseBranch(), branchName: customBranch, modelAllocations: allocations, @@ -513,7 +510,7 @@ export const NewWorktreeDialog: Component<{ if (list.length === 0) return const next = cycleVariant(effectiveVariant(), list) e.preventDefault() - setVariant(next) + setVariant(next ?? DEFAULT_VARIANT) return } undo(e) @@ -860,7 +857,7 @@ export const NewWorktreeDialog: Component<{ const next = { providerID: pid, modelID: mid } const list = Object.keys(provider.findModel(next)?.variants ?? {}) setModel(next) - setVariant(preserveVariant(current, list)) + setVariant(preserveVariant(current, list) ?? DEFAULT_VARIANT) }} onPick={restorePrompt} onCancel={restorePrompt} @@ -873,7 +870,7 @@ export const NewWorktreeDialog: Component<{ variants={variants()} value={effectiveVariant()} onSelect={setVariant} - onClear={() => setVariant(undefined)} + onClear={() => setVariant(DEFAULT_VARIANT)} allowClear clearLabel={t("common.default")} trigger={WORKTREE_PROMPT_SCOPE} diff --git a/packages/kilo-vscode/webview-ui/agent-manager/initial-message.ts b/packages/kilo-vscode/webview-ui/agent-manager/initial-message.ts index 7f210d4a560..925d79fa3b1 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/initial-message.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/initial-message.ts @@ -21,7 +21,7 @@ export function initialMessage(ev: AgentManagerSendInitialMessage): SendMessageR } export function initialVariant(ev: AgentManagerSendInitialMessage, agent: string) { - if (!ev.providerID || !ev.modelID || !ev.variant) return undefined + if (!ev.providerID || !ev.modelID || ev.variant === undefined) return undefined return { sessionID: ev.sessionId, providerID: ev.providerID, diff --git a/packages/kilo-vscode/webview-ui/src/context/session-preferences.ts b/packages/kilo-vscode/webview-ui/src/context/session-preferences.ts index 5ebe5e354a1..744489cfd51 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session-preferences.ts +++ b/packages/kilo-vscode/webview-ui/src/context/session-preferences.ts @@ -1,4 +1,5 @@ import type { Message, ModelSelection } from "../types/messages" +import { DEFAULT_VARIANT } from "./session-variant-store" export interface MessagePrefs { agent?: string @@ -17,7 +18,7 @@ export function resolveMessagePrefs(messages: Message[], names: Set): Me } if (!prefs.model && msg.role === "user" && msg.model?.providerID && msg.model.modelID) { prefs.model = { providerID: msg.model.providerID, modelID: msg.model.modelID } - prefs.variant = msg.model.variant + prefs.variant = msg.model.variant ?? DEFAULT_VARIANT } if (prefs.agent && prefs.model) break } diff --git a/packages/kilo-vscode/webview-ui/src/context/session-variant-store.ts b/packages/kilo-vscode/webview-ui/src/context/session-variant-store.ts index a1a9a9ceee3..757e0f63e6d 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session-variant-store.ts +++ b/packages/kilo-vscode/webview-ui/src/context/session-variant-store.ts @@ -39,11 +39,12 @@ export function getVariant( variants: string[], agent: string, session?: string, + configured?: string, ) { if (variants.length === 0) return undefined - const key = variantKey(sel, agent, session) - const fallback = session ? store[variantKey(sel, agent)] : undefined - const stored = store[key] ?? fallback ?? store[legacyVariantKey(sel)] + const scoped = session ? store[variantKey(sel, agent, session)] : undefined + const preset = configured && variants.includes(configured) ? configured : undefined + const stored = scoped ?? preset ?? store[variantKey(sel, agent)] ?? store[legacyVariantKey(sel)] if (stored === undefined || stored === DEFAULT_VARIANT) return undefined return preserveVariant(stored, variants) } @@ -53,9 +54,10 @@ export function getAgentVariant( sel: ModelSelection, model: { variants?: Record } | undefined, agent: string, + configured?: string, ) { if (!model?.variants) return undefined - return getVariant(store, sel, Object.keys(model.variants), agent) + return getVariant(store, sel, Object.keys(model.variants), agent, undefined, configured) } /** diff --git a/packages/kilo-vscode/webview-ui/src/context/session-variants.ts b/packages/kilo-vscode/webview-ui/src/context/session-variants.ts index 5a365be76fb..5565b6426a2 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session-variants.ts +++ b/packages/kilo-vscode/webview-ui/src/context/session-variants.ts @@ -1,5 +1,5 @@ import type { Accessor } from "solid-js" -import type { ExtensionMessage, ModelSelection } from "../types/messages" +import type { AgentConfig, ExtensionMessage, ModelSelection } from "../types/messages" import { DEFAULT_VARIANT, getAgentVariant, getVariant, preserveVariant, variantKey } from "./session-variant-store" interface Model { @@ -14,6 +14,7 @@ interface Options { selected: (sessionID?: string) => ModelSelection | null session: Accessor agent: (sessionID?: string) => string + config: (agent: string) => Pick | undefined find: (selection: ModelSelection) => Model | undefined post: (message: Message) => void listen: (handler: (message: ExtensionMessage) => void) => () => void @@ -26,9 +27,15 @@ export function createSessionVariants(options: Options) { return Object.keys(options.find(selection)?.variants ?? {}) } + const configured = (name: string, selection: ModelSelection) => { + const config = options.config(name) + if (config?.model !== `${selection.providerID}/${selection.modelID}`) return undefined + return config.variant ?? undefined + } + const agent = (name: string, selection: ModelSelection | null) => { if (!selection) return undefined - return getAgentVariant(options.selections(), selection, options.find(selection), name) + return getAgentVariant(options.selections(), selection, options.find(selection), name, configured(name, selection)) } const current = (sessionID?: string) => { @@ -37,9 +44,13 @@ export function createSessionVariants(options: Options) { if (!selection) return undefined const variants = list(sid) if (variants.length === 0) return undefined - return getVariant(options.selections(), selection, variants, options.agent(sid), sid) + const name = options.agent(sid) + return getVariant(options.selections(), selection, variants, name, sid, configured(name, selection)) } + const request = (sessionID?: string) => + current(sessionID) ?? (list(sessionID).length > 0 ? DEFAULT_VARIANT : undefined) + const select = (value: string | undefined, sessionID?: string) => { const sid = sessionID ?? options.session() const selection = options.selected(sid) @@ -75,5 +86,5 @@ export function createSessionVariants(options: Options) { return unsub } - return { carry, list, agent, current, select, load } + return { carry, list, agent, current, request, select, load } } diff --git a/packages/kilo-vscode/webview-ui/src/context/session.tsx b/packages/kilo-vscode/webview-ui/src/context/session.tsx index d1a7a5ad43e..d0c8da6a0b4 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session.tsx +++ b/packages/kilo-vscode/webview-ui/src/context/session.tsx @@ -492,6 +492,7 @@ export const SessionProvider: ParentComponent = (props) => { selected, session: currentSessionID, agent: agentForScope, + config: (agent) => config().agent?.[agent], find: provider.findModel, post: vscode.postMessage, listen: vscode.onMessage, @@ -1204,7 +1205,7 @@ export const SessionProvider: ParentComponent = (props) => { if (prefs.model && !store.sessionOverrides[sessionID]) { setStore("sessionOverrides", sessionID, prefs.model) } - if (prefs.model && prefs.variant) { + if (prefs.model && prefs.variant !== undefined) { const agent = prefs.agent ?? store.agentSelections[sessionID] ?? defaultAgent() const key = variantKey(prefs.model, agent, sessionID) if (store.variantSelections[key] === undefined) setStore("variantSelections", key, prefs.variant) @@ -2155,7 +2156,7 @@ export const SessionProvider: ParentComponent = (props) => { providerID, modelID, agent, - variant: currentVariant(scope), + variant: variants.request(scope), files, review, }) @@ -2191,7 +2192,7 @@ export const SessionProvider: ParentComponent = (props) => { providerID, modelID, agent, - variant: currentVariant(scope), + variant: variants.request(scope), files, review, agentManagerContext: context, @@ -2253,7 +2254,7 @@ export const SessionProvider: ParentComponent = (props) => { providerID: effectiveProvider, modelID: effectiveModel, agent, - variant: currentVariant(scope), + variant: variants.request(scope), files, command, commandArgs: args, @@ -2289,7 +2290,7 @@ export const SessionProvider: ParentComponent = (props) => { providerID: effectiveProvider, modelID: effectiveModel, agent, - variant: currentVariant(scope), + variant: variants.request(scope), files, agentManagerContext: context, }) From 5cbe97554ee9504946a254682c58591c75058bfd Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 28 Aug 2026 14:26:05 +0200 Subject: [PATCH 2/2] test(vscode): cover reasoning defaults in pending drafts --- packages/kilo-vscode/tests/unit/session-variants.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/kilo-vscode/tests/unit/session-variants.test.ts b/packages/kilo-vscode/tests/unit/session-variants.test.ts index f6ccfdc5bd6..670c870aaf3 100644 --- a/packages/kilo-vscode/tests/unit/session-variants.test.ts +++ b/packages/kilo-vscode/tests/unit/session-variants.test.ts @@ -86,6 +86,15 @@ describe("session variants", () => { expect(state.variants.request("session-b")).toBe("max") }) + it.each(["sidebar-pending:new", "pending:new"])("keeps a pre-submit Default choice scoped to %s", (id) => { + const state = setup(undefined, "max") + state.variants.select(undefined, id) + expect(state.variants.current(id)).toBeUndefined() + expect(state.variants.request(id)).toBe("") + expect(state.variants.current("another-draft")).toBe("max") + expect(state.messages).toEqual([]) + }) + it("persists global selections but keeps session selections local", () => { const global = setup() global.variants.select("high")