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/agent-manager-inherit-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---

Inherit the current model and reasoning variant when Agent Manager starts sessions without explicit overrides.
2 changes: 1 addition & 1 deletion packages/kilo-docs/pages/automate/agent-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ The tool supports two modes:
| `worktree` | Creates one Agent Manager git worktree and session per task |
| `local` | Creates Agent Manager sessions in the current workspace without git worktree isolation |

Each request can include 1-20 tasks. Each task must include at least one of `prompt`, `name`, or `branchName`. A task with an initial prompt can also specify a `model` (by name, e.g. `Claude Opus 4.1`) and one of that model's reasoning `variant` values. Agent Manager resolves the provider for the chosen model, preferring the provider used by the current default model and falling back to the Kilo Gateway; a qualified `provider/model` ID is also accepted to force a specific provider. Tasks without those fields use the normal model defaults. Use `versions: true` only when the tasks are alternate versions of the same work to compare; otherwise, multiple tasks start as independent sessions.
Each request can include 1-20 tasks. Each task must include at least one of `prompt`, `name`, or `branchName`. Prompted tasks inherit the model and reasoning variant used by the chat turn that starts them. A task can override that selection with a `model` (by name, e.g. `Claude Opus 4.1`) when you explicitly request a different model, or with one of the current model's reasoning `variant` values when you request a different variant. Agent Manager resolves the provider for a model override, preferring the provider used by the current turn and falling back to the Kilo Gateway; a qualified `provider/model` ID is also accepted to force a specific provider. Prepared sessions without an initial prompt use the normal model defaults. Use `versions: true` only when the tasks are alternate versions of the same work to compare; otherwise, multiple tasks start as independent sessions.

The companion `agent_manager_models` tool searches models and their supported reasoning variants on demand. Results are grouped by model name (with the offering providers listed for reference) and limited to 20 per call, so the full catalog is never added to the conversation context.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const AgentManagerModelsTool = Tool.define<
offset,
total: matches.length,
nextOffset,
hint: "Pass a model name (or one of its providers/IDs) as the agent_manager task `model`. Agent Manager picks the provider, preferring the one you use by default.",
hint: "Pass a model name (or one of its providers/IDs) as the agent_manager task `model`. Agent Manager picks the provider, preferring the one used by the current turn.",
}),
metadata: { count: models.length, total: matches.length },
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ Search the models available to Agent Manager sessions and inspect their reasonin

Use this tool before `agent_manager` when you need to pick a model or reasoning effort. Results are grouped by model, not by provider, because you select a model and Agent Manager chooses the provider for you. With no arguments it returns the top available models (capped at 20); pass `query` to search by model name or ID, and `offset` to page further. The query is matched leniently: it is case-insensitive, ignores spacing and punctuation, and is order-independent, so `opus claude`, `glm5.2`, and `gpt5` all work. You do not need the exact model name.

Each result includes the model name, its reasoning variant names, and the providers that offer it (informational only). Pass the model name back as the `agent_manager` task `model`. Agent Manager resolves the provider automatically, preferring the provider you use by default and falling back to the Kilo Gateway, so you do not need to choose a provider yourself.
Each result includes the model name, its reasoning variant names, and the providers that offer it (informational only). Pass the model name back as the `agent_manager` task `model`. Agent Manager resolves the provider automatically, preferring the provider used by the current turn and falling back to the Kilo Gateway, so you do not need to choose a provider yourself.
76 changes: 62 additions & 14 deletions packages/opencode/src/kilocode/tool/agent-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// kilocode_change - new file
import { Bus } from "@/bus"
import { AgentManagerEvent, type AgentManagerTask } from "@/kilocode/agent-manager/event"
import { KiloSessionMessageOrder } from "@/kilocode/session/message-order"
import { Provider } from "@/provider/provider"
import { Tool } from "@/tool/tool"
import { Effect, Schema } from "effect"
Expand All @@ -13,10 +14,11 @@ const Task = Schema.Struct({
branchName: Schema.optional(Schema.String).annotate({ description: "Git branch name seed for worktree mode" }),
model: Schema.optional(Schema.String).annotate({
description:
"Model name from agent_manager_models (e.g. 'Claude Opus 4.1'). Agent Manager picks the provider. A qualified provider/model ID is also accepted to force a specific provider.",
"Optional model override from agent_manager_models (e.g. 'Claude Opus 4.1'). Omit unless the user requests a different model. Agent Manager otherwise inherits the current turn's model. A qualified provider/model ID is also accepted to force a specific provider.",
}),
variant: Schema.optional(Schema.String).annotate({
description: "Reasoning variant name for this model, from agent_manager_models",
description:
"Optional reasoning variant override from agent_manager_models. Specify it without model to override the inherited model's variant. Omit both to inherit the current turn's selection.",
}),
}).check(
Schema.makeFilter((task) =>
Expand All @@ -28,7 +30,7 @@ const Task = Schema.Struct({
task.model?.trim() && !task.prompt?.trim() ? "A task model requires an initial prompt" : undefined,
),
Schema.makeFilter((task) =>
task.variant?.trim() && !task.model?.trim() ? "A task variant requires a model" : undefined,
task.variant?.trim() && !task.prompt?.trim() ? "A task variant requires an initial prompt" : undefined,
),
)

Expand All @@ -48,6 +50,7 @@ export const Params = Schema.Struct({
type Input = Schema.Schema.Type<typeof Task>
type Selected = { task?: AgentManagerTask; error?: string }
type Candidate = { providerID: string; model: Provider.Info["models"][string] }
type Source = { model: NonNullable<AgentManagerTask["model"]>; variant?: string }

function candidates(providers: Record<string, Provider.Info>): Candidate[] {
return Object.values(providers).flatMap((provider) =>
Expand Down Expand Up @@ -90,7 +93,7 @@ function suggest(all: Candidate[], value: string): string[] {
.map((entry) => entry[0])
}

// Prefer the provider the user already uses by default, then the Kilo Gateway,
// Prefer the provider the user already uses for the invoking turn, then the Kilo Gateway,
// so a model name resolves to the provider with the best chance of working
// without forcing the agent to know about provider plumbing.
function rank(providerID: string, preferred: string | undefined): number {
Expand All @@ -99,14 +102,44 @@ function rank(providerID: string, preferred: string | undefined): number {
return 2
}

function select(task: Input, all: Candidate[], preferred: string | undefined, index: number): Selected {
function select(
task: Input,
all: Candidate[],
preferred: string | undefined,
source: Source | undefined,
index: number,
): Selected {
const base = {
...(task.prompt !== undefined ? { prompt: task.prompt } : {}),
...(task.name !== undefined ? { name: task.name } : {}),
...(task.branchName !== undefined ? { branchName: task.branchName } : {}),
}
const value = task.model?.trim()
if (!value) return { task: base }
const variant = task.variant?.trim()
if (!value) {
if (!variant) {
if (!task.prompt?.trim() || !source) return { task: base }
return { task: { ...base, ...source } }
}
if (!source) {
return { error: `Task ${index + 1} variant override requires an available current model.` }
}
const active = all.find(
(item) => item.providerID === source.model.providerID && item.model.id === source.model.modelID,
)
if (!active) {
return {
error: `Task ${index + 1} current model is no longer available: ${source.model.providerID}/${source.model.modelID}. Specify a model override.`,
}
}
if (!active.model.variants || !Object.hasOwn(active.model.variants, variant)) {
const available = Object.keys(active.model.variants ?? {})
return {
error: `Task ${index + 1} variant "${variant}" is not available for ${active.model.name}. Available variants: ${available.join(", ") || "none"}`,
}
}
return { task: { ...base, model: source.model, variant } }
}

const { pool, names } = lookup(all, value)
if (pool.length === 0) {
Expand All @@ -122,7 +155,6 @@ function select(task: Input, all: Candidate[], preferred: string | undefined, in
}
}

const variant = task.variant?.trim()
const eligible = variant
? pool.filter((item) => item.model.variants && Object.hasOwn(item.model.variants, variant))
: pool
Expand All @@ -133,7 +165,12 @@ function select(task: Input, all: Candidate[], preferred: string | undefined, in
}
}

const chosen = [...eligible].sort((a, b) => rank(a.providerID, preferred) - rank(b.providerID, preferred))[0]!
const chosen = [...eligible].sort(
(a, b) =>
rank(a.providerID, preferred) - rank(b.providerID, preferred) ||
a.providerID.localeCompare(b.providerID) ||
a.model.id.localeCompare(b.model.id),
)[0]!
return {
task: {
...base,
Expand All @@ -158,15 +195,26 @@ export const AgentManagerTool = Tool.define<
parameters: Params,
execute: (params, ctx) =>
Effect.gen(function* () {
const need = params.tasks.some((task) => task.model?.trim())
const msg = KiloSessionMessageOrder.latest(ctx.messages).user
const source: Source | undefined = msg
? {
model: {
providerID: msg.model.providerID,
modelID: msg.model.modelID,
},
...(msg.model.variant ? { variant: msg.model.variant } : {}),
}
: undefined
const need = params.tasks.some((task) => task.model?.trim() || task.variant?.trim())
const all = need ? candidates(yield* provider.list()) : []
const preferred = need
? yield* provider.defaultModel().pipe(
? (source?.model.providerID ??
(yield* provider.defaultModel().pipe(
Effect.map((model) => model.providerID as string),
Effect.catch(() => Effect.succeed(undefined)),
)
)))
: undefined
const selected = params.tasks.map((task, index) => select(task, all, preferred, index))
const selected = params.tasks.map((task, index) => select(task, all, preferred, source, index))
const errors = selected.flatMap((item) => (item.error ? [item.error] : []))
if (errors.length > 0) {
return {
Expand Down Expand Up @@ -199,8 +247,8 @@ export const AgentManagerTool = Tool.define<

// Echo how each named model resolved (provider + variant) so the agent
// and the user can confirm the resolution without opening the session.
const resolved = tasks.flatMap((task) => {
if (!task.model) return []
const resolved = tasks.flatMap((task, index) => {
if (!params.tasks[index]?.model?.trim() || !task.model) return []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Variant-only overrides aren't echoed in the "Resolved models" summary

This filter (!params.tasks[index]?.model?.trim() || !task.model) only echoes a resolution when the agent passed an explicit model override, matching the pre-existing behavior for named-model resolution. But with this PR, a task can also carry an explicit variant-only override that changes the effective reasoning variant while inheriting the model from the invoking turn (see the select() branch a few lines above returning { ...base, model: source.model, variant }). Since params.tasks[index]?.model is empty for that case, the variant override is silently applied without appearing in the "Resolved models:" output, so the agent/user has no confirmation that the variant was actually changed (as opposed to falling back to the inherited default).

Consider also echoing when params.tasks[index]?.variant?.trim() is set, not just model.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

const name = all.find(
(item) => item.providerID === task.model!.providerID && item.model.id === task.model!.modelID,
)?.model.name
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/kilocode/tool/agent-manager.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Modes:
- `worktree`: creates a new Agent Manager git worktree for each task, like the New Worktree dialog.
- `local`: creates Agent Manager sessions in the current workspace directory without git worktree isolation.

Each task may provide a prompt, a short display name, a branch name, a `model`, and a model-specific reasoning `variant`. Specify `model` by name (e.g. "Claude Opus 4.1"); the name is matched leniently (case-insensitive, punctuation/spacing-insensitive, order-independent), so an approximate name like "opus 4.1" works and you do not need the exact name. Agent Manager picks the provider for you, preferring the provider you use by default and falling back to the Kilo Gateway. A qualified `provider/model` ID is also accepted to force a specific provider. If the name is ambiguous and matches several different models, the tool returns the candidates so you can choose. A model selection requires an initial prompt so the session can persist that selection. Keep display names short because Agent Manager cards are narrow. Branch names are sanitized before worktree creation. Use `agent_manager_models` to search available models and variants on demand instead of guessing or loading the full model catalog. Tasks that omit `model` and `variant` use the normal defaults. The agent and base branch settings always use the normal defaults.
Each task may provide a prompt, a short display name, a branch name, a `model`, and a model-specific reasoning `variant`. By default, omit `model` and `variant`: prompted tasks inherit the exact model and reasoning variant used by the current turn. Only specify `model` when the user explicitly asks to use or compare a different model, and only specify `variant` when the user explicitly asks for a different reasoning variant. A variant can be specified without a model to override the inherited model's variant. Never choose a different model merely because work is being fanned out. Specify an override `model` by name (e.g. "Claude Opus 4.1"); the name is matched leniently (case-insensitive, punctuation/spacing-insensitive, order-independent), so an approximate name like "opus 4.1" works and you do not need the exact name. Agent Manager picks the provider for you, preferring the provider used by the current turn and falling back to the Kilo Gateway. A qualified `provider/model` ID is also accepted to force a specific provider. If the name is ambiguous and matches several different models, the tool returns the candidates so you can choose. A model or variant selection requires an initial prompt so the session can persist that selection. Keep display names short because Agent Manager cards are narrow. Branch names are sanitized before worktree creation. Use `agent_manager_models` to search available models and variants on demand instead of guessing or loading the full model catalog. Prepared sessions without an initial prompt use the normal defaults. The agent and base branch settings always use the normal defaults.

By default, multiple tasks are started as independent Agent Manager sessions. Set `versions` to true only when all tasks are alternate versions of the same work that should be compared together. Versioned worktrees are grouped in Agent Manager and branch names may receive version suffixes.

Expand Down
Loading
Loading