diff --git a/.changeset/subagent-model-override.md b/.changeset/subagent-model-override.md new file mode 100644 index 0000000000..07a918c8dc --- /dev/null +++ b/.changeset/subagent-model-override.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/agent-core": minor +--- + +Enable the secondary-model feature by default (opt out with `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0` or `[experimental] secondary-model = false`), and let the `Agent`/`AgentSwarm` `model` parameter and profile `modelPreference`/`model_preference` name any configured `[models]` alias — unknown aliases fail the tool call at spawn time with the valid choices listed. diff --git a/.changeset/v2-subagent-model-override.md b/.changeset/v2-subagent-model-override.md new file mode 100644 index 0000000000..6ec91d9054 --- /dev/null +++ b/.changeset/v2-subagent-model-override.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/agent-core-v2": minor +--- + +Enable the secondary-model feature by default (opt out with `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0` or `[experimental] secondary-model = false`), and let the `Agent`/`AgentSwarm` `model` parameter and profile `modelPreference`/`model_preference` name any configured `[models]` alias — unknown aliases fail the tool call at spawn time with the valid choices listed. diff --git a/docs/en/configuration/config-files.md b/docs/en/configuration/config-files.md index b671922578..ff8deb8f5a 100644 --- a/docs/en/configuration/config-files.md +++ b/docs/en/configuration/config-files.md @@ -190,9 +190,9 @@ You can also switch models temporarily without touching the config file — by s ## `secondary_model` -The secondary model is a second model pointer next to the primary `default_model` — typically a cheaper model that features can bind to when they do not need the main model. Its consumer today is subagent spawning: when set, newly spawned subagents (`Agent` / `AgentSwarm`) bind to it by default instead of inheriting the main agent's model, and the main agent is told it can pick per spawn between `"secondary"` (this model) and `"primary"` (the main model). When unset, subagents inherit the main agent's model. +The secondary model is a second model pointer next to the primary `default_model` — typically a cheaper model that features can bind to when they do not need the main model. Its consumer today is subagent spawning: when set, newly spawned subagents (`Agent` / `AgentSwarm`) bind to it by default instead of inheriting the main agent's model, and the main agent is told it can pick per spawn between `"secondary"` (this model), `"primary"` (the main model), or any configured `[models]` alias. When unset, subagents inherit the main agent's model. -This feature is experimental and disabled by default. Enable it with `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=1`, or the master `KIMI_CODE_EXPERIMENTAL_FLAG=1`. It takes effect in every launch mode, including the interactive TUI. +This feature is enabled by default. Disable it with `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0` (or `[experimental] secondary-model = false` in `config.toml`) to restore the legacy behavior where subagents always inherit the main agent's model; an explicit per-spawn alias choice is still honored. It takes effect in every launch mode, including the interactive TUI. In the interactive TUI, the [`/secondary_model`](../reference/slash-commands.md) command opens a model picker that writes this section and live-applies it to the current session, so newly spawned subagents bind the new secondary model right away. @@ -213,7 +213,7 @@ max_output_size = 8192 `model` / `default_effort` can be overridden by the `KIMI_SECONDARY_MODEL` / `KIMI_SECONDARY_EFFORT` environment variables, which take higher priority than `config.toml`. -When the experiment is enabled, the configuration is validated as the session starts: an unresolvable `model`, or a `default_effort` not listed by the (patched) model, produces a startup warning (also returned by the session-warnings API). The check is advisory — a broken secondary model still fails at spawn time, with the same source hint attached to the spawn error. +When the feature is enabled, the configuration is validated as the session starts: an unresolvable `model`, or a `default_effort` not listed by the (patched) model, produces a startup warning (also returned by the session-warnings API). The check is advisory — a broken secondary model still fails at spawn time, with the same source hint attached to the spawn error. ## `thinking` diff --git a/docs/en/configuration/env-vars.md b/docs/en/configuration/env-vars.md index 9fb803d56c..c3031b90a0 100644 --- a/docs/en/configuration/env-vars.md +++ b/docs/en/configuration/env-vars.md @@ -128,7 +128,7 @@ Switches that control the behavior of subsystems such as telemetry, background t | `KIMI_CODE_PLUGIN_MARKETPLACE_URL` | Override the plugin marketplace JSON loaded by `/plugins`; useful for dev loopback servers, staging CDN files, or alternate marketplace directories | `https://code.kimi.com/kimi-code/plugins/marketplace.json`; also accepts `http://`, `file://` URLs, and local paths | | `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` | Cap how many AgentSwarm subagents run concurrently during the initial ramp; leave unset for no cap | Positive integer; invalid values fail fast | | `KIMI_SUBAGENT_TIMEOUT_MS` | Maximum wall-clock time (ms) a single subagent (`Agent` / `AgentSwarm`) may run; takes higher priority than `[subagent] timeout_ms` in `config.toml` (default `7200000`, i.e. 2 hours) | Positive integer; invalid values fall back to the config or default | -| `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL` | Enable the experimental secondary-model feature in every launch mode, including the interactive TUI; the master `KIMI_CODE_EXPERIMENTAL_FLAG=1` also enables it | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` | +| `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL` | Toggle the secondary-model feature in every launch mode, including the interactive TUI; the feature is enabled by default, so set a falsy value to restore the legacy behavior where subagents always inherit the main agent's model. The master `KIMI_CODE_EXPERIMENTAL_FLAG=1` force-enables it | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` | | `KIMI_SECONDARY_MODEL` | Secondary model; takes higher priority than `[secondary_model] model` in `config.toml`. When the secondary-model experiment is enabled, newly spawned subagents (`Agent` / `AgentSwarm`) bind to it by default instead of inheriting the main agent's model | A model id from your configured `[models]`, e.g. `kimi-code/kimi-k2.5`; blank values are ignored | | `KIMI_SECONDARY_EFFORT` | Thinking effort for the secondary model; takes higher priority than `[secondary_model] default_effort` in `config.toml` and applies only when both the model and its experiment are enabled | An effort value, e.g. `low`; blank values are ignored | | `KIMI_MCP_STARTUP_TIMEOUT_MS` | Global default connection timeout (ms) for all MCP servers; takes higher priority than `[mcp] startup_timeout_ms` in `config.toml`, but a per-server `startupTimeoutMs` in `mcp.json` still wins (default `30000`) | Integer from `1` to `2147483647`; invalid values are ignored | diff --git a/docs/en/customization/agents.md b/docs/en/customization/agents.md index b9c67039dd..2209d0f035 100644 --- a/docs/en/customization/agents.md +++ b/docs/en/customization/agents.md @@ -100,7 +100,7 @@ You are a strict code reviewer. Read the diff, then report findings grouped by s | `description` | yes | What the agent does. Shown to the main Agent when it picks a sub-agent, so write it to guide delegation decisions | | `whenToUse` | no | Extra hint describing when the agent should be used | | `override` | no | Whether this file may replace a same-name built-in Agent. Defaults to `false`; `--agent-file` is already explicit and does not require this field | -| `model_preference` | no | Symbolic default used when `Agent` or `AgentSwarm` spawns this profile: `primary` selects the caller's main model, while `secondary` selects `[secondary_model] model`. An explicit tool-call `model` wins; without either setting, the configured secondary model remains the default. If no secondary model is configured, the subagent inherits the caller's model | +| `model_preference` | no | Default model choice used when `Agent` or `AgentSwarm` spawns this profile: `primary` selects the caller's main model, `secondary` selects `[secondary_model] model`, and any other value names a concrete `[models]` alias (validated at spawn time — an unknown alias fails the spawn with the valid choices). An explicit tool-call `model` wins; without either setting, the configured secondary model remains the default. If no secondary model is configured, the subagent inherits the caller's model | | `tools` | no | Allowlist of tool names such as `Read` or `Bash`; MCP tools are matched with globs such as `mcp__github__*`. Accepts a YAML list or a comma-separated string (`tools: Read, Grep`). Omit to allow all tools; a lone `*` also allows all tools; an empty list (`tools: []`) disables all tools | | `disallowedTools` | no | Denylist with the same syntax and matching rules, applied after `tools` | | `subagents` | no | Allowlist of sub-agent names this agent may delegate to, with the same syntax as `tools` (YAML list or comma-separated string). Omit to allow every type; a lone `*` also allows all types | @@ -111,7 +111,7 @@ The body is the agent's system prompt, and it is rendered as a template each tim Unknown fields are ignored, so newer files stay readable by older versions. Fields from other agent tools (such as Claude Code's `model` or OpenCode's `mode`) are ignored the same way, the comma-separated `tools` form keeps Claude Code-style agent files loadable, and a missing `name` falls back to the file name so OpenCode-style files load too — a minimal file with `description` and a body works across tools. -`model_preference` applies only to newly spawned subagents when the secondary-model experiment is enabled — set `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=1`, or the master `KIMI_CODE_EXPERIMENTAL_FLAG=1`. It takes effect in every launch mode, including the interactive TUI. The field never names a concrete model alias, and resumed subagents keep their existing model. The selected preference is shown to the main agent alongside the profile description so it can still pass an explicit `model` when a task needs a different choice. +`model_preference` applies only to newly spawned subagents; the secondary-model feature it builds on is enabled by default (disable it with `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0` or `[experimental] secondary-model = false` in `config.toml`). It takes effect in every launch mode, including the interactive TUI. The field may name a concrete `[models]` alias, whose existence is checked when the subagent is spawned, and resumed subagents keep their existing model. The selected preference is shown to the main agent alongside the profile description so it can still pass an explicit `model` when a task needs a different choice. A file with invalid content discovered in a directory is skipped with a warning and does not affect other files. A file passed explicitly via `--agent-file` must be valid — otherwise the CLI reports the error and exits. diff --git a/docs/en/reference/tools.md b/docs/en/reference/tools.md index 8b412b5366..339279e508 100644 --- a/docs/en/reference/tools.md +++ b/docs/en/reference/tools.md @@ -89,9 +89,9 @@ Collaboration tools handle inter-Agent coordination, user interaction, and Skill | `AskUserQuestion` | Auto-allow | Ask the user a question to gather structured input | | `Skill` | Auto-allow | Invoke a registered inline Skill | -**`Agent`** delegates a subtask to a sub-Agent. Required parameters: `prompt` (complete task description) and `description` (a 3–5 word short summary). Optional parameters: `subagent_type` (defaults to `coder`), `resume` (ID of an existing Agent to resume; mutually exclusive with `subagent_type`), `run_in_background` (defaults to false), and `model` (`"secondary"` for the secondary model configured via `[secondary_model] model`, or `"primary"` for the main model; ignored when resuming; available when the secondary-model experiment is enabled). An explicit `model` overrides the selected [agent profile's `model_preference`](../customization/agents.md#agent-file-format); without either, the configured secondary model is the default, or the subagent inherits the caller's model when no secondary model is configured. Agent tasks time out after 2 hours by default; the limit is configurable via `[subagent] timeout_ms` in `config.toml` (`0` = no timeout, or the `KIMI_SUBAGENT_TIMEOUT_MS` env var), and defaults to no timeout in print mode (`kimi -p`). In foreground mode the parent Agent waits for the sub-Agent to complete before continuing; in background mode a task ID is returned immediately and the result is automatically delivered back to the main Agent via a synthetic User message when done. When several foreground `Agent` calls run in the same step, the TUI groups them and shows each subagent's running, waiting, completed, or failed status with elapsed time. See [Agent & Sub-Agents](../customization/agents.md) for details. +**`Agent`** delegates a subtask to a sub-Agent. Required parameters: `prompt` (complete task description) and `description` (a 3–5 word short summary). Optional parameters: `subagent_type` (defaults to `coder`), `resume` (ID of an existing Agent to resume; mutually exclusive with `subagent_type`), `run_in_background` (defaults to false), and `model` (`"secondary"` for the secondary model configured via `[secondary_model] model`, `"primary"` for the main model, or any configured `[models]` alias to run the subagent on that model; ignored when resuming). An explicit `model` overrides the selected [agent profile's `model_preference`](../customization/agents.md#agent-file-format); without either, the configured secondary model is the default, or the subagent inherits the caller's model when no secondary model is configured. Agent tasks time out after 2 hours by default; the limit is configurable via `[subagent] timeout_ms` in `config.toml` (`0` = no timeout, or the `KIMI_SUBAGENT_TIMEOUT_MS` env var), and defaults to no timeout in print mode (`kimi -p`). In foreground mode the parent Agent waits for the sub-Agent to complete before continuing; in background mode a task ID is returned immediately and the result is automatically delivered back to the main Agent via a synthetic User message when done. When several foreground `Agent` calls run in the same step, the TUI groups them and shows each subagent's running, waiting, completed, or failed status with elapsed time. See [Agent & Sub-Agents](../customization/agents.md) for details. -**`AgentSwarm`** launches subagents from a shared `prompt_template` and an `items` array, resumes existing subagents through `resume_agent_ids`, or combines both in one call. The template must contain the `{{item}}` placeholder; each item replaces that placeholder and launches one new subagent. Pass `subagent_type` to choose the profile used by every spawned subagent in the swarm, or omit it to use `coder`. Pass `model` (available when the secondary-model experiment is enabled) to run item-spawned subagents on the secondary model configured via `[secondary_model] model` (`"secondary"`) or the main model (`"primary"`). This explicit choice overrides the selected [agent profile's `model_preference`](../customization/agents.md#agent-file-format); without either, the configured secondary model is the default, or the subagent inherits the caller's model when no secondary model is configured. Resumed subagents keep their own model. Without `resume_agent_ids`, the tool requires at least 2 items; with `resume_agent_ids`, it can resume one or more existing subagents. The tool supports up to 128 total subagents, waits for all subagents to finish, and returns an aggregated report. In the TUI, foreground swarms show a live `Agent swarm` progress panel above the input box. If a model response calls `AgentSwarm`, that call must be the only tool call in the response; to run multiple swarms, call one `AgentSwarm`, wait for its result, then call the next, or combine the work into one swarm when a single template can cover it. In `manual` permission mode, `AgentSwarm` calls outside active swarm mode request approval unless a permission rule allows them; while swarm mode is active, `AgentSwarm` itself is auto-approved. Permission rules match `AgentSwarm` by tool name only — argument patterns such as `AgentSwarm(swarm)` are not supported. By default the tool ramps up concurrency without an upper limit (5 subagents start immediately, then 1 more every 700 ms); set `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` to a positive integer to cap how many subagents run at the same time during that ramp, or leave it unset for no cap. If it is set to a value that is not a positive integer, the AgentSwarm call fails fast. +**`AgentSwarm`** launches subagents from a shared `prompt_template` and an `items` array, resumes existing subagents through `resume_agent_ids`, or combines both in one call. The template must contain the `{{item}}` placeholder; each item replaces that placeholder and launches one new subagent. Pass `subagent_type` to choose the profile used by every spawned subagent in the swarm, or omit it to use `coder`. Pass `model` to run item-spawned subagents on the secondary model configured via `[secondary_model] model` (`"secondary"`), the main model (`"primary"`), or any configured `[models]` alias. This explicit choice overrides the selected [agent profile's `model_preference`](../customization/agents.md#agent-file-format); without either, the configured secondary model is the default, or the subagent inherits the caller's model when no secondary model is configured. Resumed subagents keep their own model. Without `resume_agent_ids`, the tool requires at least 2 items; with `resume_agent_ids`, it can resume one or more existing subagents. The tool supports up to 128 total subagents, waits for all subagents to finish, and returns an aggregated report. In the TUI, foreground swarms show a live `Agent swarm` progress panel above the input box. If a model response calls `AgentSwarm`, that call must be the only tool call in the response; to run multiple swarms, call one `AgentSwarm`, wait for its result, then call the next, or combine the work into one swarm when a single template can cover it. In `manual` permission mode, `AgentSwarm` calls outside active swarm mode request approval unless a permission rule allows them; while swarm mode is active, `AgentSwarm` itself is auto-approved. Permission rules match `AgentSwarm` by tool name only — argument patterns such as `AgentSwarm(swarm)` are not supported. By default the tool ramps up concurrency without an upper limit (5 subagents start immediately, then 1 more every 700 ms); set `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` to a positive integer to cap how many subagents run at the same time during that ramp, or leave it unset for no cap. If it is set to a value that is not a positive integer, the AgentSwarm call fails fast. **`AskUserQuestion`** asks the user a structured multiple-choice question — useful for disambiguation or option selection. The `questions` parameter accepts 1–4 questions; each question requires `question` (ending with `?`), `options` (2–4 choices, each with a `label` and `description`), and optional `header` (max 12 characters) and `multi_select` (defaults to false). An "Other" option is appended automatically. Setting `background` to true starts a background question task and returns a task ID immediately. When the host does not support interactive questioning, a failure message is returned and the Agent should ask the user directly in a text reply instead. diff --git a/docs/zh/configuration/config-files.md b/docs/zh/configuration/config-files.md index 478959acf6..f087313d2e 100644 --- a/docs/zh/configuration/config-files.md +++ b/docs/zh/configuration/config-files.md @@ -190,9 +190,9 @@ display_name = "Kimi for Coding (custom)" ## `secondary_model` -次主力模型是主模型 `default_model` 之外的第二个模型指针——通常是一个更便宜的模型,供不需要主模型的功能绑定使用。目前的消费者是子 Agent 派生:设置后,新派生的子 Agent(`Agent` / `AgentSwarm`)默认绑定该模型,而不再继承主 Agent 的模型;主 Agent 会被告知每次派生可在 `"secondary"`(该模型)与 `"primary"`(主模型)之间选择。未设置时,子 Agent 继承主 Agent 的模型。 +次主力模型是主模型 `default_model` 之外的第二个模型指针——通常是一个更便宜的模型,供不需要主模型的功能绑定使用。目前的消费者是子 Agent 派生:设置后,新派生的子 Agent(`Agent` / `AgentSwarm`)默认绑定该模型,而不再继承主 Agent 的模型;主 Agent 会被告知每次派生可在 `"secondary"`(该模型)、`"primary"`(主模型)或任意已配置的 `[models]` 别名之间选择。未设置时,子 Agent 继承主 Agent 的模型。 -该功能目前是实验功能,默认关闭。通过 `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=1` 启用,或使用 master `KIMI_CODE_EXPERIMENTAL_FLAG=1`。它在包括交互式 TUI 在内的所有启动方式下生效。 +该功能默认启用。如需恢复子 Agent 始终继承主 Agent 模型的旧行为,可通过 `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0`(或 `config.toml` 中的 `[experimental] secondary-model = false`)关闭;显式指定模型别名的派生选择仍然生效。它在包括交互式 TUI 在内的所有启动方式下生效。 在交互式 TUI 中,可以使用 [`/secondary_model`](../reference/slash-commands.md) 命令打开模型选择器来设置该配置:选择后会写入本小节配置,并在当前会话立即生效——之后派生的子 Agent 会直接绑定新的第二模型。 @@ -213,7 +213,7 @@ max_output_size = 8192 `model` / `default_effort` 可被环境变量 `KIMI_SECONDARY_MODEL` / `KIMI_SECONDARY_EFFORT` 覆盖,优先级均高于配置文件。 -实验功能启用后,会话启动时会校验该配置:`model` 无法解析,或 `default_effort` 不在(应用补丁后的)模型 effort 列表中时,会在启动时显示警告(并通过会话警告 API 返回)。该检查仅为提示——配置有误的次主力模型仍会在派生子 Agent 时失败,派生错误中同样附带配置来源提示。 +功能启用后,会话启动时会校验该配置:`model` 无法解析,或 `default_effort` 不在(应用补丁后的)模型 effort 列表中时,会在启动时显示警告(并通过会话警告 API 返回)。该检查仅为提示——配置有误的次主力模型仍会在派生子 Agent 时失败,派生错误中同样附带配置来源提示。 ## `thinking` diff --git a/docs/zh/configuration/env-vars.md b/docs/zh/configuration/env-vars.md index 2b5c461a4b..7501576600 100644 --- a/docs/zh/configuration/env-vars.md +++ b/docs/zh/configuration/env-vars.md @@ -128,8 +128,8 @@ kimi | `KIMI_CODE_PLUGIN_MARKETPLACE_URL` | 覆盖 `/plugins` 加载的 plugin marketplace JSON,适合 dev loopback server、测试 CDN 文件或替换 marketplace 目录 | `https://code.kimi.com/kimi-code/plugins/marketplace.json`;也接受 `http://`、`file://` URL 和本地路径 | | `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` | 限制 AgentSwarm 初始提升并发阶段可同时运行的子 Agent 数量;不设置表示不限制 | 正整数;非法值会立即失败 | | `KIMI_SUBAGENT_TIMEOUT_MS` | 单个子 Agent(`Agent` / `AgentSwarm`)可运行的最长时间(毫秒);优先级高于 `config.toml` 的 `[subagent] timeout_ms`(默认 `7200000`,即 2 小时) | 正整数;非法值回退到配置或默认值 | -| `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL` | 在包括交互式 TUI 在内的所有启动方式下启用实验性的次主力模型功能;master `KIMI_CODE_EXPERIMENTAL_FLAG=1` 也会启用本功能 | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` | -| `KIMI_SECONDARY_MODEL` | 次主力模型;优先级高于 `config.toml` 的 `[secondary_model] model`。次主力模型实验功能启用后,新派生的子 Agent 默认绑定该模型,而不再继承主 Agent 的模型 | 已配置 `[models]` 中的模型 id,如 `kimi-code/kimi-k2.5`;空白值被忽略 | +| `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL` | 在包括交互式 TUI 在内的所有启动方式下开关次主力模型功能;该功能默认启用,设置假值可恢复子 Agent 总是继承主 Agent 模型的旧行为。master `KIMI_CODE_EXPERIMENTAL_FLAG=1` 会强制启用本功能 | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` | +| `KIMI_SECONDARY_MODEL` | 次主力模型;优先级高于 `config.toml` 的 `[secondary_model] model`。次主力模型功能启用时(默认启用),新派生的子 Agent(`Agent` / `AgentSwarm`)默认绑定该模型,而不再继承主 Agent 的模型 | 已配置 `[models]` 中的模型 id,如 `kimi-code/kimi-k2.5`;空白值被忽略 | | `KIMI_SECONDARY_EFFORT` | 次主力模型的 thinking effort;优先级高于 `config.toml` 的 `[secondary_model] default_effort`,仅在次主力模型及其实验功能均启用时生效 | effort 取值,如 `low`;空白值被忽略 | | `KIMI_MCP_STARTUP_TIMEOUT_MS` | 所有 MCP server 的全局默认连接超时(毫秒);优先级高于 `config.toml` 的 `[mcp] startup_timeout_ms`,但低于 `mcp.json` 中单个 server 的 `startupTimeoutMs`(默认 `30000`) | `1` 到 `2147483647` 的整数;非法值被忽略 | | `KIMI_MCP_TOOL_TIMEOUT_MS` | 所有 MCP server 的全局默认单次工具调用超时(毫秒);优先级高于 `config.toml` 的 `[mcp] tool_timeout_ms`,但低于 `mcp.json` 中单个 server 的 `toolTimeoutMs`(默认 `60000`) | `1` 到 `2147483647` 的整数;非法值被忽略 | diff --git a/docs/zh/customization/agents.md b/docs/zh/customization/agents.md index 9a935e94fb..a77f246225 100644 --- a/docs/zh/customization/agents.md +++ b/docs/zh/customization/agents.md @@ -100,7 +100,7 @@ disallowedTools: | `description` | 是 | Agent 的用途。主 Agent 挑选子 Agent 时会看到,请围绕委派决策来写 | | `whenToUse` | 否 | 补充说明何时应使用该 Agent | | `override` | 否 | 是否允许覆盖同名内置 Agent,默认 `false`。`--agent-file` 属于显式启动意图,无需设置此字段 | -| `model_preference` | 否 | `Agent` 或 `AgentSwarm` 启动该 profile 时的符号默认值:`primary` 选择调用方的主模型,`secondary` 选择 `[secondary_model] model`。工具调用显式传入的 `model` 优先;两者均未设置时,已配置的次主力模型仍为默认值。未配置次主力模型时,子 Agent 继承调用方模型 | +| `model_preference` | 否 | `Agent` 或 `AgentSwarm` 启动该 profile 时的默认模型选择:`primary` 选择调用方的主模型,`secondary` 选择 `[secondary_model] model`,其他取值则指定一个具体的 `[models]` 别名(在派生时校验——未知别名会使派生失败并列出可选项)。工具调用显式传入的 `model` 优先;两者均未设置时,已配置的次主力模型仍为默认值。未配置次主力模型时,子 Agent 继承调用方模型 | | `tools` | 否 | 工具名允许列表,如 `Read`、`Bash`;MCP 工具用 glob 匹配,如 `mcp__github__*`。支持 YAML 列表或逗号分隔字符串(`tools: Read, Grep`)两种写法。缺省表示允许全部工具;单独的 `*` 同样表示允许全部工具;空列表(`tools: []`)表示禁用全部工具 | | `disallowedTools` | 否 | 禁止列表,写法与匹配规则相同,在 `tools` 之后应用 | | `subagents` | 否 | 允许委派的子 Agent 名称列表,写法与 `tools` 相同(YAML 列表或逗号分隔字符串)。缺省表示可委派所有类型;单独的 `*` 同样表示全部 | @@ -111,7 +111,7 @@ disallowedTools: 未知字段会被忽略,新版本写的文件在旧版本上仍可读取。其他 Agent 工具的字段(如 Claude Code 的 `model`、OpenCode 的 `mode`)同样会被忽略;加上 `tools` 的逗号分隔写法和 `name` 缺省回退到文件名,Claude Code 与 OpenCode 风格的 Agent 文件一般可直接加载 —— 只含 `description` 和正文的最小文件可跨工具通用。 -`model_preference` 仅在次主力模型实验功能启用时对新启动的子 Agent 生效——设置 `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=1`,或 master `KIMI_CODE_EXPERIMENTAL_FLAG=1`。它在包括交互式 TUI 在内的所有启动方式下生效。该字段不用于填写具体模型 alias,已恢复的子 Agent 也会保持原模型。主 Agent 会在 profile 描述中看到这项偏好,因此仍可在某项任务需要不同选择时显式传入 `model`。 +`model_preference` 仅对新启动的子 Agent 生效;它依赖的次主力模型功能默认启用(可用 `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0` 或 `config.toml` 中的 `[experimental] secondary-model = false` 关闭)。它在包括交互式 TUI 在内的所有启动方式下生效。该字段可以指定一个具体的 `[models]` 别名,其有效性在派生子 Agent 时校验;已恢复的子 Agent 会保持原模型。主 Agent 会在 profile 描述旁看到这项偏好,因此仍可在某项任务需要不同选择时显式传入 `model`。 目录中发现的非法文件会被跳过并告警,不影响其他文件。通过 `--agent-file` 显式传入的文件必须合法 —— 否则 CLI 会报错并退出。 diff --git a/docs/zh/reference/tools.md b/docs/zh/reference/tools.md index 009ff3d052..48c645d561 100644 --- a/docs/zh/reference/tools.md +++ b/docs/zh/reference/tools.md @@ -89,9 +89,9 @@ Plan 模式是一种受约束的工作状态:进入后 `Write` 与 `Edit` 只 | `AskUserQuestion` | 自动放行 | 向用户提问以获取结构化输入 | | `Skill` | 自动放行 | 调用已注册的 inline Skill | -**`Agent`** 将子任务委托给子 Agent 执行。必填参数:`prompt`(完整任务描述)和 `description`(3–5 个词的简短说明)。可选参数:`subagent_type`(默认 `coder`)、`resume`(恢复已有 Agent 的 ID,与 `subagent_type` 互斥)、`run_in_background`(默认 false)和 `model`(`"secondary"` 表示 `[secondary_model] model` 配置的次主力模型,`"primary"` 表示主模型;resume 时无效;次主力模型实验功能启用后可用)。显式 `model` 会覆盖所选 [Agent profile 的 `model_preference`](../customization/agents.md#agent-文件格式);两者均未设置时,已配置的次主力模型为默认值,未配置时则继承调用方模型。Agent 任务默认 2 小时超时,可通过 `config.toml` 的 `[subagent] timeout_ms`(`0` = 无超时,或 `KIMI_SUBAGENT_TIMEOUT_MS` 环境变量)配置,且在 print 模式(`kimi -p`)下默认无超时。前台模式下父 Agent 等待子 Agent 完成再继续;后台模式立即返回任务 ID,完成时通过合成 User 消息自动回到主 Agent。多个前台 `Agent` 调用在同一步运行时,TUI 会合并展示,并为每个子 Agent 显示运行、等待、完成或失败状态以及已耗时长。子 Agent 体系细节见 [Agent 与子 Agent](../customization/agents.md)。 +**`Agent`** 将子任务委托给子 Agent 执行。必填参数:`prompt`(完整任务描述)和 `description`(3–5 个词的简短说明)。可选参数:`subagent_type`(默认 `coder`)、`resume`(恢复已有 Agent 的 ID,与 `subagent_type` 互斥)、`run_in_background`(默认 false)和 `model`(`"secondary"` 表示 `[secondary_model] model` 配置的次主力模型,`"primary"` 表示主模型,或填写任意已配置的 `[models]` 别名让子 Agent 运行在该模型上;resume 时无效)。显式 `model` 会覆盖所选 [Agent profile 的 `model_preference`](../customization/agents.md#agent-文件格式);两者均未设置时,已配置的次主力模型为默认值,未配置时则继承调用方模型。Agent 任务默认 2 小时超时,可通过 `config.toml` 的 `[subagent] timeout_ms`(`0` = 无超时,或 `KIMI_SUBAGENT_TIMEOUT_MS` 环境变量)配置,且在 print 模式(`kimi -p`)下默认无超时。前台模式下父 Agent 等待子 Agent 完成再继续;后台模式立即返回任务 ID,完成时通过合成 User 消息自动回到主 Agent。多个前台 `Agent` 调用在同一步运行时,TUI 会合并展示,并为每个子 Agent 显示运行、等待、完成或失败状态以及已耗时长。子 Agent 体系细节见 [Agent 与子 Agent](../customization/agents.md)。 -**`AgentSwarm`** 可以从共享的 `prompt_template` 和 `items` 数组启动子 Agent,也可以通过 `resume_agent_ids` 恢复已有子 Agent,或在一次调用中同时使用两者。模板必须包含 `{{item}}` 占位符;每个 item 会替换该占位符,并启动一个新的子 Agent。传入 `subagent_type` 可以指定整个 swarm 中所有新启动的子 Agent 使用的 profile;省略时默认使用 `coder`。传入 `model`(次主力模型实验功能启用后可用)可以让新启动的子 Agent 运行在 `[secondary_model] model` 配置的次主力模型(`"secondary"`)或主模型(`"primary"`)上。这项显式选择会覆盖所选 [Agent profile 的 `model_preference`](../customization/agents.md#agent-文件格式);两者均未设置时,已配置的次主力模型为默认值,未配置时则继承调用方模型。恢复的子 Agent 保持其原有模型。不传 `resume_agent_ids` 时,本工具要求至少 2 个 item;传入 `resume_agent_ids` 时,可以恢复 1 个或多个已有子 Agent。本工具最多支持 128 个子 Agent,会等待全部子 Agent 完成,并返回聚合报告。在 TUI 中,前台 swarm 会在输入框上方显示实时 `Agent swarm` 进度面板。若一次模型响应调用 `AgentSwarm`,该调用必须是该响应中的唯一工具调用;如需运行多个 swarm,应先调用一个 `AgentSwarm` 并等待结果,再调用下一个,若单个模板可以覆盖这些工作,也可以合并为一个 swarm。在 `manual` 权限模式下,未处于 swarm mode 时调用 `AgentSwarm` 会触发审批,除非已有权限规则允许;swarm mode 已开启时,`AgentSwarm` 本身会自动放行。权限规则只能按工具名 `AgentSwarm` 匹配,不支持 `AgentSwarm(swarm)` 这类参数模式。默认情况下,本工具会逐步提升并发且不设上限(立即启动 5 个子 Agent,之后每 700 毫秒再启动 1 个);将 `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` 设为正整数可限制该阶段同时运行的子 Agent 数量,不设置则表示不限制。若设置为非正整数的值,本次 AgentSwarm 调用会立即失败。 +**`AgentSwarm`** 可以从共享的 `prompt_template` 和 `items` 数组启动子 Agent,也可以通过 `resume_agent_ids` 恢复已有子 Agent,或在一次调用中同时使用两者。模板必须包含 `{{item}}` 占位符;每个 item 会替换该占位符,并启动一个新的子 Agent。传入 `subagent_type` 可以指定整个 swarm 中所有新启动的子 Agent 使用的 profile;省略时默认使用 `coder`。传入 `model` 可以让新启动的子 Agent 运行在 `[secondary_model] model` 配置的次主力模型(`"secondary"`)、主模型(`"primary"`)或任意已配置的 `[models]` 别名上。这项显式选择会覆盖所选 [Agent profile 的 `model_preference`](../customization/agents.md#agent-文件格式);两者均未设置时,已配置的次主力模型为默认值,未配置时则继承调用方模型。恢复的子 Agent 保持其原有模型。不传 `resume_agent_ids` 时,本工具要求至少 2 个 item;传入 `resume_agent_ids` 时,可以恢复 1 个或多个已有子 Agent。本工具最多支持 128 个子 Agent,会等待全部子 Agent 完成,并返回聚合报告。在 TUI 中,前台 swarm 会在输入框上方显示实时 `Agent swarm` 进度面板。若一次模型响应调用 `AgentSwarm`,该调用必须是该响应中的唯一工具调用;如需运行多个 swarm,应先调用一个 `AgentSwarm` 并等待结果,再调用下一个,若单个模板可以覆盖这些工作,也可以合并为一个 swarm。在 `manual` 权限模式下,未处于 swarm mode 时调用 `AgentSwarm` 会触发审批,除非已有权限规则允许;swarm mode 已开启时,`AgentSwarm` 本身会自动放行。权限规则只能按工具名 `AgentSwarm` 匹配,不支持 `AgentSwarm(swarm)` 这类参数模式。默认情况下,本工具会逐步提升并发且不设上限(立即启动 5 个子 Agent,之后每 700 毫秒再启动 1 个);将 `KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY` 设为正整数可限制该阶段同时运行的子 Agent 数量,不设置则表示不限制。若设置为非正整数的值,本次 AgentSwarm 调用会立即失败。 **`AskUserQuestion`** 以结构化多选题的形式向用户提问,适用于需要消歧或选择方案的场景。`questions` 参数接受 1–4 道题,每道题需提供 `question`(以 `?` 结尾)、`options`(2–4 个选项,每项含 `label` 和 `description`)以及可选的 `header`(最多 12 字符)和 `multi_select`(默认 false)。系统自动附加"其他"选项。`background` 为 true 时启动后台问题任务并立即返回任务 ID。宿主未实现交互式提问能力时返回失败提示,Agent 应改为在文本回复中直接提问。 diff --git a/packages/agent-core-v2/src/agent/tools/agent-swarm/agent-swarm.ts b/packages/agent-core-v2/src/agent/tools/agent-swarm/agent-swarm.ts index b0f777dd63..6501e452fa 100644 --- a/packages/agent-core-v2/src/agent/tools/agent-swarm/agent-swarm.ts +++ b/packages/agent-core-v2/src/agent/tools/agent-swarm/agent-swarm.ts @@ -54,10 +54,10 @@ export const AgentSwarmToolInputSchema = z 'Map of existing subagent agent_id to the prompt used to resume that subagent. These resumed subagents are launched before new item-based subagents.', ), model: z - .enum(['secondary', 'primary']) + .string() .optional() .describe( - 'Which model to run the item-spawned subagents on: "secondary" = the configured secondary model; "primary" = the main model you are running on (for hard, quality-sensitive tasks). This explicit choice overrides the selected agent type\'s model_preference; without either, secondary is the default when configured. Only effective when a secondary model is configured; otherwise subagents inherit your model. Resumed subagents always keep their own model.', + 'Which model to run the item-spawned subagents on: "secondary" = the configured secondary model; "primary" = the main model you are running on (for hard, quality-sensitive tasks); or pass any configured model alias from your [models] config. This explicit choice overrides the selected agent type\'s model_preference; without either, secondary is the default when configured. Only effective when a secondary model is configured or a concrete alias is passed; otherwise subagents inherit your model. Resumed subagents always keep their own model.', ), }) .strict(); diff --git a/packages/agent-core-v2/src/agent/tools/agent/agent.ts b/packages/agent-core-v2/src/agent/tools/agent/agent.ts index 8e480bd1ec..b198706e74 100644 --- a/packages/agent-core-v2/src/agent/tools/agent/agent.ts +++ b/packages/agent-core-v2/src/agent/tools/agent/agent.ts @@ -57,10 +57,10 @@ export const SubagentToolInputSchema = z.preprocess( 'If true, return immediately without waiting for completion. Prefer false unless the task can run independently and there is a clear benefit to not waiting.', ), model: z - .enum(['secondary', 'primary']) + .string() .optional() .describe( - 'Which model to run the subagent on: "secondary" = the configured secondary model; "primary" = the main model you are running on (for hard, quality-sensitive tasks). This explicit choice overrides the selected agent type\'s model_preference; without either, secondary is the default when configured. Only effective when a secondary model is configured; otherwise the subagent inherits your model. Ignored when resuming — resumed subagents keep their own model.', + 'Which model to run the subagent on: "secondary" = the configured secondary model; "primary" = the main model you are running on (for hard, quality-sensitive tasks); or pass any configured model alias from your [models] config to run the subagent on that model. This explicit choice overrides the selected agent type\'s model_preference; without either, secondary is the default when configured. Only effective when a secondary model is configured or a concrete alias is passed; otherwise the subagent inherits your model. Ignored when resuming — resumed subagents keep their own model.', ), }), ); diff --git a/packages/agent-core-v2/src/agent/tools/agent/agentTool.ts b/packages/agent-core-v2/src/agent/tools/agent/agentTool.ts index ff249b2f59..0a26baca4a 100644 --- a/packages/agent-core-v2/src/agent/tools/agent/agentTool.ts +++ b/packages/agent-core-v2/src/agent/tools/agent/agentTool.ts @@ -12,7 +12,7 @@ * constants, `ISubagentTool`) lives in `./agent`. * * Spawn bindings use an explicit tool choice first, then the target profile's - * symbolic model preference, before `resolveSubagentBinding` falls back to the + * model preference, before `resolveSubagentBinding` falls back to the * configured secondary model or the caller's model. The selected alias is * resolved through the model catalog before lifecycle allocation. A resumed * agent keeps the model recorded in its own wire journal — with per-subagent diff --git a/packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts b/packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts index c0735dcdf8..2a8dc5c2af 100644 --- a/packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts +++ b/packages/agent-core-v2/src/app/agentProfileCatalog/agentProfileCatalog.ts @@ -10,11 +10,11 @@ * * Every profile is self-contained: `systemPrompt(context)` returns the complete * prompt (base + role overlay are merged at definition time, not at spawn - * time). Profiles stay independent of concrete model aliases, but may declare - * a symbolic primary/secondary preference used as the default when spawned as - * a subagent. The builtin {@link DEFAULT_AGENT_PROFILE_NAME} (`agent`) is the - * default profile used when an Agent is bound to a Model without naming a - * profile. + * time). Profiles stay independent of concrete model wiring, but may declare + * a model preference — symbolic (`primary` / `secondary`) or a concrete + * `[models]` id — used as the default when spawned as a subagent. The + * builtin {@link DEFAULT_AGENT_PROFILE_NAME} (`agent`) is the default + * profile used when an Agent is bound to a Model without naming a profile. * * `tools` is an allowlist of exact builtin names plus `mcp__` globs * (`undefined` = every tool active); `disallowedTools` denies with the same @@ -38,7 +38,15 @@ import type { ISessionProcessRunner } from '#/session/process/processRunner'; export const DEFAULT_AGENT_PROFILE_NAME = 'agent'; -export type AgentModelPreference = 'primary' | 'secondary'; +/** + * Model preference a profile declares for subagent spawning: the symbolic + * `primary` / `secondary` choices or a concrete `[models]` id. A concrete id + * is validated at binding-resolution time, not at profile-load time (profiles + * can load before the config is final). The `Agent` / `AgentSwarm` tools use + * it as the default for their `model` parameter when the call does not pass + * one explicitly. + */ +export type AgentModelPreference = 'primary' | 'secondary' | (string & {}); export interface AgentProfilePromptPrefixContext { readonly cwd: string; diff --git a/packages/agent-core-v2/src/session/subagent/configSection.ts b/packages/agent-core-v2/src/session/subagent/configSection.ts index a850f5d221..9cf7db54aa 100644 --- a/packages/agent-core-v2/src/session/subagent/configSection.ts +++ b/packages/agent-core-v2/src/session/subagent/configSection.ts @@ -11,10 +11,15 @@ * domain — resolve their per-run timeout through `resolveSubagentTimeoutMs`, * and render the timeout message with `formatSubagentTimeoutDescription`. * - * The model half of the spawn binding is the secondary model (the section - * and type in `app/kosongConfig` — `[secondary_model]` on disk): when its - * experiment is enabled and the model is set, newly spawned subagents bind to - * it by default instead of inheriting the caller's model, and the + * The model half of the spawn binding covers three request shapes: the + * symbolic `primary` (the caller's own model, inherited as before) and + * `secondary` (the `[secondary_model]` recipe — the section and type in + * `app/kosongConfig`), plus any other string, which is treated as a concrete + * `[models]` id and validated up front — an unknown id fails the spawn with + * an error listing the valid choices rather than silently falling back to + * the caller's model. When the secondary-model experiment is enabled (the + * default) and the recipe's model is set, newly spawned subagents bind to it + * by default instead of inheriting the caller's model, and the * `Agent`/`AgentSwarm` tools let the parent model pick per spawn via their * `model` parameter. When unset, spawning behavior is unchanged (subagents * inherit the caller's model). A recipe with patch fields binds the @@ -23,7 +28,7 @@ * explicit subagent thinking; without it the subagent resolves thinking * naturally (global thinking config → the bound model's default effort) * rather than inheriting the caller's level. Both tools resolve spawn - * bindings through `resolveSubagentBinding`, advertise the pair via + * bindings through `resolveSubagentBinding`, advertise the choices via * `buildSubagentModelDescriptions`, and wrap spawn failures with * `wrapSubagentModelError`. Self-registered at module load via * `registerConfigSection`, so the `config` domain never imports this @@ -36,6 +41,7 @@ import { Error2, ErrorCodes, isError2 } from '#/errors'; import type { AgentModelPreference } from '#/app/agentProfileCatalog/agentProfileCatalog'; import type { IFlagService } from '#/app/flag/flag'; import { + MODELS_SECTION, SECONDARY_MODEL_ENV, SECONDARY_MODEL_SECTION, } from '#/app/kosongConfig/configSection'; @@ -44,6 +50,7 @@ import { secondaryModelPatch, } from '#/app/kosongConfig/secondaryModelOverlay'; import { type SecondaryModelConfig } from '#/app/kosongConfig/configSection'; +import type { ModelsSection } from '#/kosong/model/model'; import { type EnvBindings, envBindings, @@ -110,12 +117,37 @@ export function resolveSecondaryModel( return config.get(SECONDARY_MODEL_SECTION); } +/** + * The concrete model ids a subagent may be bound to explicitly: every + * configured `[models]` entry except the synthesized secondary derived entry + * (an internal runtime artifact bound through the `secondary` choice). + */ +function explicitModelIds(config: IConfigService): string[] { + const models = config.get(MODELS_SECTION) ?? {}; + return Object.keys(models).filter((id) => id !== SECONDARY_DERIVED_MODEL_ID); +} + +function unknownSubagentModelMessage(requested: string, modelIds: readonly string[]): string { + const choices = ['primary', 'secondary', ...modelIds].map((choice) => `"${choice}"`).join(', '); + return `Unknown subagent model "${requested}". Pass one of: ${choices}.`; +} + export function resolveSubagentBinding( config: IConfigService, flags: IFlagService, own: { modelAlias: string; thinkingLevel: string }, requested?: SubagentModelChoice, ): { model: string; thinking?: string } { + if (requested !== undefined && requested !== 'primary' && requested !== 'secondary') { + // A concrete [models] id: honor it directly (flag-independent — an + // explicit choice is never silently ignored) and fail loudly on typos. + const modelIds = explicitModelIds(config); + if (!modelIds.includes(requested)) { + throw new Error(unknownSubagentModelMessage(requested, modelIds)); + } + const record = config.get(MODELS_SECTION)?.[requested]; + return { model: requested, thinking: record?.defaultEffort ?? own.thinkingLevel }; + } const secondary = resolveSecondaryModel(config, flags); if (requested !== 'primary' && secondary?.model !== undefined) { return { @@ -134,13 +166,27 @@ export function buildSubagentModelDescriptions( flags: IFlagService, callerModelAlias: string | undefined, ): string | undefined { + if (callerModelAlias === undefined) return undefined; const secondaryModel = resolveSecondaryModel(config, flags)?.model; - if (secondaryModel === undefined || callerModelAlias === undefined) return undefined; - return [ - 'Available models (pass via model):', - `- secondary: ${secondaryModel} (default) — the configured secondary model; prefer it for routine subagent tasks`, + const modelIds = explicitModelIds(config).filter( + (id) => id !== callerModelAlias && id !== secondaryModel, + ); + if (secondaryModel === undefined && modelIds.length === 0) return undefined; + const lines = ['Available models (pass via model):']; + if (secondaryModel !== undefined) { + lines.push( + `- secondary: ${secondaryModel} (default) — the configured secondary model; prefer it for routine subagent tasks`, + ); + } + lines.push( `- primary: ${callerModelAlias} — the main model you are running on; use it for hard, quality-sensitive subagent tasks`, - ].join('\n'); + ); + if (modelIds.length > 0) { + lines.push( + `- ${modelIds.join(', ')} — configured [models] aliases; pass one to run the subagent on that specific model`, + ); + } + return lines.join('\n'); } export function wrapSubagentModelError( diff --git a/packages/agent-core-v2/src/session/subagent/flag.ts b/packages/agent-core-v2/src/session/subagent/flag.ts index 72ba140500..ccf1908240 100644 --- a/packages/agent-core-v2/src/session/subagent/flag.ts +++ b/packages/agent-core-v2/src/session/subagent/flag.ts @@ -3,10 +3,10 @@ * into `flag`. * * Gates secondary-model selection for newly spawned subagents, including the - * agent-facing model choices and startup validation warning. Off by default; - * enable via `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL`, the master - * `KIMI_CODE_EXPERIMENTAL_FLAG`, or the `[experimental]` config section. - * Imported for its side effect from the package barrel. + * agent-facing model choices and startup validation warning. On by default; + * disable via `KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL` or the + * `[experimental]` config section to restore legacy inherit-the-parent-model + * behavior. Imported for its side effect from the package barrel. */ import { type FlagDefinitionInput, registerFlagDefinition } from '#/app/flag/flagRegistry'; @@ -18,9 +18,9 @@ export const secondaryModelFlag: FlagDefinitionInput = { id: SECONDARY_MODEL_FLAG_ID, title: 'Secondary model for subagents', description: - 'Let newly spawned subagents use a separately configured secondary model by default, with an explicit primary-model override for quality-sensitive tasks.', + 'Let newly spawned subagents use a separately configured secondary model by default, with an explicit per-spawn model override (primary or any configured [models] alias) for quality-sensitive tasks. Enabled by default; disable to restore legacy inherit-the-parent-model behavior.', env: SECONDARY_MODEL_FLAG_ENV, - default: false, + default: true, surface: 'core', }; diff --git a/packages/agent-core-v2/src/workspace/workspaceAgentProfileLoader/internal/agentFile.ts b/packages/agent-core-v2/src/workspace/workspaceAgentProfileLoader/internal/agentFile.ts index cacb0cf686..4f173cc78f 100644 --- a/packages/agent-core-v2/src/workspace/workspaceAgentProfileLoader/internal/agentFile.ts +++ b/packages/agent-core-v2/src/workspace/workspaceAgentProfileLoader/internal/agentFile.ts @@ -118,10 +118,16 @@ function parseModelPreference( filePath: string, ): AgentFileDefinition['modelPreference'] { if (value === undefined || value === null) return undefined; - if (value === 'primary' || value === 'secondary') return value; - throw new AgentFileParseError( - `Frontmatter field "model_preference" in ${filePath} must be "primary" or "secondary"`, - ); + // "primary" / "secondary" or a concrete [models] id. The id is validated + // at binding-resolution time, not here (profiles can load before the + // config is final). + const parsed = nonEmptyString(value); + if (parsed === undefined) { + throw new AgentFileParseError( + `Frontmatter field "model_preference" in ${filePath} must be "primary", "secondary", or a configured model alias`, + ); + } + return parsed; } function parseBoolean(value: unknown, field: string, filePath: string): boolean { diff --git a/packages/agent-core-v2/test/agent/loop/loop.test.ts b/packages/agent-core-v2/test/agent/loop/loop.test.ts index 783a8b49cb..6d973cb274 100644 --- a/packages/agent-core-v2/test/agent/loop/loop.test.ts +++ b/packages/agent-core-v2/test/agent/loop/loop.test.ts @@ -104,8 +104,8 @@ describe('Agent loop', () => { [emit] turn.step.started { "turnId": 0, "step": 1, "stepId": "" } [emit] agent.activity.updated { "lifecycle": "ready", "turn": { "turnId": 0, "origin": { "kind": "user" }, "phase": "running", "step": 1, "ending": false, "pendingApprovals": [], "activeToolCalls": [], "since": "