Merge pull request #13546 from Kilo-Org/validate-and-improve-pr-13539-fix

fix(vscode): honor configured reasoning defaults
This commit is contained in:
Marius
2026-08-28 15:20:21 +02:00
committed by GitHub
11 changed files with 137 additions and 39 deletions
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Use configured agent reasoning in new sessions instead of outdated saved defaults, and preserve explicit Default selections.
@@ -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"]])
})
})
@@ -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: "",
})
})
})
@@ -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()
@@ -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<string, string> = {}
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,48 @@ 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.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")
@@ -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<string | undefined>(
fallback(saved.variant, () => session.variantForAgent(initialAgent, initialModel)),
)
const [variant, setVariant] = createSignal<string | undefined>(saved.variant)
const [sandbox, setSandbox] = createSignal<boolean | undefined>(saved.sandbox)
const [sandboxDefault, setSandboxDefault] = createSignal<boolean | undefined>()
const [sandboxOverride, setSandboxOverride] = createSignal<boolean | undefined>()
@@ -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}
@@ -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,
@@ -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<string>): 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
}
@@ -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<string, unknown> } | 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)
}
/**
@@ -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<string | undefined>
agent: (sessionID?: string) => string
config: (agent: string) => Pick<AgentConfig, "model" | "variant"> | 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 }
}
@@ -493,6 +493,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,
@@ -1206,7 +1207,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)
@@ -2162,7 +2163,7 @@ export const SessionProvider: ParentComponent = (props) => {
providerID,
modelID,
agent,
variant: currentVariant(scope),
variant: variants.request(scope),
files,
review,
})
@@ -2198,7 +2199,7 @@ export const SessionProvider: ParentComponent = (props) => {
providerID,
modelID,
agent,
variant: currentVariant(scope),
variant: variants.request(scope),
files,
review,
agentManagerContext: context,
@@ -2260,7 +2261,7 @@ export const SessionProvider: ParentComponent = (props) => {
providerID: effectiveProvider,
modelID: effectiveModel,
agent,
variant: currentVariant(scope),
variant: variants.request(scope),
files,
command,
commandArgs: args,
@@ -2296,7 +2297,7 @@ export const SessionProvider: ParentComponent = (props) => {
providerID: effectiveProvider,
modelID: effectiveModel,
agent,
variant: currentVariant(scope),
variant: variants.request(scope),
files,
agentManagerContext: context,
})