Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-agent-reasoning-defaults.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Use configured agent reasoning in new sessions instead of outdated saved defaults, and preserve explicit Default selections.
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -12,7 +12,7 @@ describe("Agent Manager initial message", () => {
providerID: "anthropic",
modelID: "claude-sonnet-4",
agent: "code",
variant: "high",
variant,
})

expect(msg).toEqual({
Expand All @@ -23,7 +23,7 @@ describe("Agent Manager initial message", () => {
providerID: "anthropic",
modelID: "claude-sonnet-4",
agent: "code",
variant: "high",
variant,
files: undefined,
})
})
Expand All @@ -38,15 +38,15 @@ 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",
sessionId: "session-a",
worktreeId: "wt-a",
providerID: "anthropic",
modelID: "claude-sonnet-4",
variant: "medium",
variant,
},
"code",
)
Expand All @@ -56,7 +56,7 @@ describe("Agent Manager initial message", () => {
providerID: "anthropic",
modelID: "claude-sonnet-4",
agent: "code",
value: "medium",
value: variant,
})
})

Expand All @@ -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(
Expand All @@ -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"]])
})
})
14 changes: 13 additions & 1 deletion packages/kilo-vscode/tests/unit/session-preferences.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
Expand Down Expand Up @@ -66,7 +78,7 @@ describe("session preference recovery", () => {
expect(prefs).toEqual({
agent: "code",
model: { providerID: "anthropic", modelID: "claude-sonnet-4" },
variant: undefined,
variant: "",
})
})
})
25 changes: 25 additions & 0 deletions packages/kilo-vscode/tests/unit/session-variant-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
50 changes: 47 additions & 3 deletions packages/kilo-vscode/tests/unit/session-variants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = []
Expand All @@ -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)
Expand All @@ -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", () => {
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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>()
Expand All @@ -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) => {
Expand All @@ -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
})

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Message, ModelSelection } from "../types/messages"
import { DEFAULT_VARIANT } from "./session-variant-store"

export interface MessagePrefs {
agent?: string
Expand All @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
}

/**
Expand Down
Loading
Loading