Skip to content

feat: replace the secondary-model experiment with a declarative subagent model pool - #2700

Open
7Sageer wants to merge 12 commits into
mainfrom
feat/subagent-model-pool
Open

feat: replace the secondary-model experiment with a declarative subagent model pool#2700
7Sageer wants to merge 12 commits into
mainfrom
feat/subagent-model-pool

Conversation

@7Sageer

@7Sageer 7Sageer commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No tracking issue — the problem is explained below.

Problem

The experimental secondary model ([secondary_model] recipe + KIMI_SECONDARY_MODEL / KIMI_SECONDARY_EFFORT env vars + the agent-file model_preference field) gives subagents a single fallback model and is wired through SDK session-apply APIs (Session.applyPersistedSecondaryModel, a synthesized __secondary__ model entry). The main agent cannot choose a subagent model per spawn, and the recipe does not fit the v2 engine's config-section architecture.

What changed

v2 engine — declarative subagent model pool in [secondary_model]

  • [secondary_model.models] maps configured [models] aliases to the selection hints shown to the main agent; [secondary_model] default_model names the fallback used when a spawn passes no model. A lone default_model (no models table) forms an implicit single-entry pool — the minimal "secondary model" configuration.
  • The Agent / AgentSwarm tools gain a model parameter (advertised only when a pool exists): any pool alias, or the reserved symbolic primary, which binds the caller's own model and thinking level. Pool-alias bindings carry no explicit thinking level — thinking resolves naturally (global [thinking] config → the bound model's default effort). Per-entry thinking levels reuse the existing model-overrides mechanism: register a variant alias with [models."<alias>".overrides] default_effort and list it in the pool.
  • Pool validation fails loudly (CONFIG_INVALID) at session create / resume / fork on a missing or out-of-pool default_model, an unresolvable alias, or a reserved primary key, with a Session-scope validation service as backstop. Tool descriptions mark [default] / [main model]; the caller's alias stays a normal pool entry because only primary inherits the caller's thinking level.
  • The pool keys briefly lived under [subagent] during this PR's iteration; leftover [subagent] default_model / models keys are declared as deprecations pointing to [secondary_model] and no longer apply.
  • The secondary-model experiment is removed from the v2 engine, the SDK (Session.applyPersistedSecondaryModel, SECONDARY_DERIVED_MODEL_ALIAS), kap-server, and the agent-file model_preference field on v2.

TUI

  • /secondary-model (renamed from /secondary_model): opens a model picker and persists [secondary_model] default_model, adding the picked alias to an existing [secondary_model.models] pool with an empty description when missing (the engine requires the default to be a pool key). The picker hides the no-op Thinking footer; the reserved primary alias is rejected with a rename hint. No live-apply step — the pool resolves per spawn, so the next subagent dispatch picks the new value up.

kap-server

  • /api/v1/config accepts and echoes the subagent and secondary_model domains; the snake→camel patch conversion now preserves user-defined map keys under providers / models / experimental / raw (an alias like fast_model previously became fastModel), without leaking preserve mode into a colliding alias's own fields. The __secondary__ derived-entry hiding is gone together with the synthesized entry itself.

Default (v1) engine

  • The section is now shared: the v1 schema learns secondary_model.defaultModel / models so a shared config.toml round-trips, and secondaryModelPatch excludes them from the recipe patch. v1 keeps resolving subagent models through the recipe keys (model, default_effort, the env vars) and ignores the pool at runtime.

Docs & changesets: en/zh config-files pages document the pool semantics, the deprecation path from [subagent], and the thinking-variant recipe (a second alias with an overridden default_effort); tools and slash-commands pages updated; two changesets (minor for @moonshot-ai/kimi-code and @moonshot-ai/kimi-code-sdk).

Behavior examples

Configuration — minimal form (every subagent binds kimi-hs by default) vs a full pool:

# Minimal: a lone default_model is an implicit single-entry pool
[secondary_model]
default_model = "kimi-hs"

# Full pool: the main agent picks per spawn
[secondary_model]
default_model = "kimi-hs"
[secondary_model.models]
kimi-hs = "fast and cheap; daily refactoring, explanations, small edits"
k3 = "frontend work"
fable = "hard reasoning tasks"

What the main agent sees — the Agent / AgentSwarm tool description gains a model parameter and renders the pool (here the caller itself runs k3):

Available models (pass via model):
- kimi-hs [default]: fast and cheap; daily refactoring, explanations, small edits
- k3 [main model]: frontend work
- fable: hard reasoning tasks
- primary (k3): the main model you are running on, bound with your current thinking level; use it for hard, quality-sensitive subagent tasks

The two k3 lines differ only in thinking inheritance: a pool-alias binding carries no explicit thinking level (the subagent resolves it from global config / the model's default effort), while primary inherits the caller's model and current thinking level.

/secondary-model — interactive-only path to the minimal form:

  • Opens the model picker with the Thinking footer hidden (the selection carries no thinking level); picking a model writes [secondary_model] default_model.
  • If a [secondary_model.models] table exists and the picked alias is not in it, the alias is added with an empty description (the engine requires the default to be a pool key); without a table only default_model is written.
  • The reserved alias primary is rejected with a rename hint instead of writing a pool the engine would refuse.
  • No live-apply step: the pool resolves per spawn, so the next subagent dispatch picks the new value up without a session restart.
  • v2 engine only — the default engine ignores [secondary_model] default_model and keeps resolving subagent models through the recipe keys (model, default_effort).

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.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

7Sageer added 4 commits August 5, 2026 11:33
Add a declarative subagent model pool to agent-core-v2: [subagent.models]
maps [models] entry ids to selection hints rendered in the Agent/AgentSwarm
tool descriptions, and [subagent].default_model picks the spawn model when
the caller passes none. The tools' model parameter becomes a free-form
alias string (stripped when no pool is configured), description rendering
is caller-aware (primary (alias) [main model]), and a session-start
validation service fails fast with CONFIG_INVALID on a missing/invalid
default_model or an unresolvable pool alias.

Remove the secondary-model experiment from the v2 engine, node-sdk,
kap-server, and the TUI (the /secondary_model command), and drop the
agent-profile modelPreference / model_preference frontmatter field on v2.
The legacy v1 engine keeps the experiment unchanged; v2 ignores leftover
[secondary_model] config silently.
…icker mapping

Deep-review follow-ups to the [subagent.models] pool:

- validate the pool before session materialization (after config.ready)
  and before the fork file copy, so a broken pool no longer leaves
  orphaned session dirs or leaked MCP overlay connections; the
  Session-scope validation service stays as a backstop
- reject the reserved "primary" pool alias at startup, and again
  defensively in resolveSubagentBinding so a pool broken by a runtime
  config edit fails loudly at spawn instead of binding the wrong model
- keep the [default] marker when the caller's own model is the pool
  default (primary (alias) [main model] [default])
- recompile the cached tool-args validator when a tool advertises a new
  schema object (mid-session pool edits no longer hit a stale validator)
- map config.invalid to VALIDATION_FAILED in kap-server's session routes,
  the debug transport mapper, and the catch-all error handler
- hide the v1-synthesized __secondary__ entry from the /model and
  /provider pickers again
- fold per-export doc blocks into file headers per package comment
  conventions; add pre-flight/reserved-key/validator/mapping tests and
  document that create/resume/fork all fail on a broken pool
- v2 engine: a pool-less [subagent] default_model forms an implicit
  single-entry pool — validated at session create/resume/fork like an
  explicit pool, and advertised through the Agent/AgentSwarm model
  parameter.
- Tool descriptions: the caller's own alias is a normal pool entry
  marked [main model]; the primary line stays distinct because only it
  inherits the caller's thinking level.
- TUI: /secondary_model returns, persisting [subagent] default_model
  (merging into an existing pool with an empty description); the picker
  hides the no-op Thinking footer and rejects the reserved primary
  alias.
- kap-server: /api/v1/config accepts and echoes subagent; the
  snake-to-camel patch conversion preserves user-defined map keys under
  providers/models/experimental/raw without leaking preserve mode into
  a colliding alias's own fields.
- v1 config schema learns subagent.defaultModel/models so the shared
  config.toml round-trips; the v1 engine still ignores them at runtime.
- Docs (en/zh) and changesets updated.
@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: add3738

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

This PR includes changesets to release 4 packages
Name Type
@moonshot-ai/kimi-code-sdk Minor
@moonshot-ai/kimi-code Minor
@moonshot-ai/acp-adapter Patch
kimi-code 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

7Sageer added 6 commits August 6, 2026 20:27
The pool keys (default_model, [secondary_model.models]) now live in their
own [secondary_model] config section instead of [subagent], which keeps
only timeout_ms; legacy [subagent] pool keys are ignored with a
deprecation warning. The SDK config contract carries the pool on the
secondaryModel field, so the TUI /secondary-model command (now also
aliased /subagent-model) and the kap-server /config wire read and write
it directly with no translation layer.
@7Sageer
7Sageer marked this pull request as ready for review August 11, 2026 08:49
@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@add3738
npx https://pkg.pr.new/@moonshot-ai/kimi-code@add3738

commit: add3738

@7Sageer 7Sageer changed the title feat: replace the secondary-model experiment with the [subagent] model pool feat: replace the secondary-model experiment with a declarative subagent model pool Aug 11, 2026
…odel] force

force = true removes the main agent's per-spawn model choice: the Agent
and AgentSwarm tools stop advertising the model parameter and every spawn
binds default_model; an explicit choice, "primary" included, is rejected.
The setting requires default_model, rejects a [secondary_model.models]
table, and is validated loudly at session create/resume/fork (lifecycle
preflight plus the Session-scope backstop). The v1 engine declares the
key for write round-trips and excludes it from the recipe patch.

Also documents pool entries as per-alias thinking-level variants via
default_effort overrides.
The pool examples invented aliases (kimi-hs, fable, codex) and referenced
non-existent model IDs (model = "codex"); they now reference only the
managed aliases provisioned by /login (kimi-code/k3,
kimi-code/kimi-for-coding, kimi-code/kimi-for-coding-highspeed), with the
effort variant derived as kimi-for-coding-highspeed-deep. Also replaces the
versioned kimi-k2.5 alias with kimi-for-coding per the docs model-ID rule.
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.

1 participant