Skip to content

feat(agent-core): enable per-subagent model selection by default and allow [models] aliases - #2439

Open
imadcat wants to merge 1 commit into
MoonshotAI:mainfrom
imadcat:feat/subagent-model-override
Open

feat(agent-core): enable per-subagent model selection by default and allow [models] aliases#2439
imadcat wants to merge 1 commit into
MoonshotAI:mainfrom
imadcat:feat/subagent-model-override

Conversation

@imadcat

@imadcat imadcat commented Jul 31, 2026

Copy link
Copy Markdown

Related Issue

Resolve #2438

Problem

Subagents spawned via the Agent / AgentSwarm tools always run on the same model as the main agent unless the operator discovers and enables the experimental secondary-model flag, which is off by default. Even then, the model parameter only accepts the symbolic primary / secondary choices — no way to point one subagent at an arbitrary configured [models] alias — and agent profiles cannot name a concrete model. See linked issue.

What changed

Covers both engines (agent-core and agent-core-v2, parity-tested):

  • Promoted the secondary-model experiment to default-on (flags/registry.ts, session/subagent/flag.ts). Users who never configure [secondary_model] see zero behavior change (the existing inherit-parent fallback is untouched); the legacy behavior stays available via KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0 or [experimental] secondary-model = false.
  • model parameter accepts any configured [models] alias in Agent and AgentSwarm (v1 collaboration/agent.ts / agent-swarm.ts, v2 tools/agent / agent-swarm), in addition to the symbolic primary / secondary. A concrete alias is honored flag-independently — an explicit choice is never silently ignored.
  • Profiles can name a concrete alias via modelPreference / agent-file model_preference (v1 profile/types.ts, v2 agentProfileCatalog.ts), validated at binding-resolution time rather than profile-load time.
  • Unknown aliases fail loudly at spawn: resolveSubagentBinding throws before the child agent is created, so the tool call rejects with e.g. Unknown subagent model "typo". Pass one of: "primary", "secondary", "main", "cheap". instead of silently inheriting the parent's model or failing mid-turn.
  • Tool descriptions advertise the configured aliases (buildSubagentModelDescriptions in both engines) so the parent model can pick per spawn.
  • Thinking effort for an explicit alias uses the alias's default_effort when configured, otherwise inherits the caller's level (symmetric in both engines).

This builds on the existing secondary-model plumbing rather than adding a parallel mechanism, which keeps the diff small and both engines symmetric.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works. (agent-core: test/session/subagent-host.test.ts, test/config/secondary-model.test.ts, test/tools/agent.test.ts, test/profile/agentfile.test.ts; agent-core-v2: test/app/config/config.test.ts, test/tool/tool.test.ts, test/workspace/workspaceAgentProfileLoader/agentFile.test.ts; full engine suites pass: 510 files / 8452 tests, plus the v1↔v2 parity suite.)
  • Ran gen-changesets skill, or this PR needs no changeset. (minor changesets for @moonshot-ai/agent-core and @moonshot-ai/agent-core-v2)
  • Ran gen-docs skill, or this PR needs no doc update. (docs/en + docs/zh: reference/tools.md, customization/agents.md, configuration/config-files.md, configuration/env-vars.md)

…allow [models] aliases

Subagents can now run on a different model than the main agent out of
the box:

- The secondary-model experiment is promoted to default-on in both
  agent-core and agent-core-v2 (opt out via
  KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0 or
  [experimental] secondary-model = false).
- The Agent/AgentSwarm 'model' parameter accepts any configured
  [models] alias in addition to the symbolic 'primary'/'secondary'.
- Profile modelPreference (agent-file 'model_preference') likewise
  accepts a concrete alias, validated at binding-resolution time.
- Unknown aliases fail the spawn up front with a tool-visible error
  listing the valid choices instead of silently inheriting the
  parent's model.
- Tool descriptions now advertise the configured aliases so the
  parent model can pick per spawn.
@changeset-bot

changeset-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8ce2d93

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@moonshot-ai/agent-core Minor
@moonshot-ai/agent-core-v2 Minor
@moonshot-ai/acp-adapter Patch
@moonshot-ai/migration-legacy Patch
@moonshot-ai/kap-server Patch
@moonshot-ai/klient Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ce2d93f32

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

throw new Error(unknownSubagentModelMessage(requested, modelIds));
}
const record = config.get<ModelsSection>(MODELS_SECTION)?.[requested];
return { model: requested, thinking: record?.defaultEffort ?? own.thinkingLevel };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor override default_effort for alias choices

When a configured alias stores its user-pinned default_effort under [models."<alias>".overrides], passing that alias through the new model/model_preference path reaches this branch but reads only the raw top-level record.defaultEffort, so the subagent inherits the caller's thinking level instead of using the alias's effective default. The model resolver already merges overrides via effectiveModelConfig/effectiveModelAlias, so this branch should use the same effective model view; the v1 helper has the same direct raw read and should be kept in parity.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Subagents cannot easily run on a different model than the main agent

1 participant