Model-selected subagent routes
模型选择的 subagent 路由
`dsh-tool-subagent` can configure child `AgentOptions`, and both in-process providers merge those values over the parent Agent's LLM selection. The model-facing tool could not request a different provider, model, or reasoning effort for one suitable subtask. Loading one distinctly named delegation tool per LLM route duplicates schemas and turns a per-call scheduling choice into deployment configuration. The model als
English
Problem
dsh-tool-subagent can configure child AgentOptions, and both in-process providers merge those values over the parent Agent's LLM selection. The model-facing tool could not request a different provider, model, or reasoning effort for one suitable subtask. Loading one distinctly named delegation tool per LLM route duplicates schemas and turns a per-call scheduling choice into deployment configuration.
The model also needs a bounded way to discover live providers and model-owned effort ids. Rendering the adapter directory into every delegation description would make an advisory, mutable catalog part of the prompt prefix.
Decision
dsh-tool-subagent exposes optional provider, model, and reasoning_effort fields only when its Agent-scoped modelSelectionSettings instance resolves a Session policy and the bound subagent provider advertises SubagentCapabilities.agentOptions. The policy uses the exact user authorization owned by user-authorized subagent model routes; there is no unrestricted static mode. Disabled instances omit and reject model-facing selection, while configured Config.agentOptions remain deployment-owned defaults. A settings-enabled instance against a provider without the capability fails the plugin mount.
Provider and model form one route and must be supplied together. An effort may be supplied alone when configured, parent, or provider-owned route defaults provide the effective route. Static provider.agentRouteDefaults, when present, establish the provider/model baseline; Config.agentOptions and model arguments overlay it before route-aware effort clearing. Providers without static defaults use compatible fields from the parent Agent's latest logged request selection, with creation options supplying the fallback before its first request and retaining the configured output-token limit. Reasoning-effort identifiers remain adapter-owned. An unchanged route inherits an omitted effort only from the selected baseline; changing provider or model without naming an effort clears the lower layer's route-owned value so the selected model resolves its own default. AgentOptions carries the resulting effort into the child loop, whose request header logs the effective value. A continuable descriptor records it with the resolved provider and model so a child that has not logged its first request can cold-resume with the same selection.
An explicit or configured provider, model, or effort resolves through ctx.llm.resolveCallConfig() after the provider baseline and request precedence are complete. Providers with static route defaults suppress parent-effort inheritance when the request omits effort, preserving the selected model's default. The LLM lookup owns provider registration, exact-model metadata, reasoning-effort validation, and adapter defaults. After the asynchronous lookup, the tool checks cancellation and confirms the same provider instance remains registered before creating a child or background job, so HMR cannot combine one provider's defaults with another provider's process. Calls with no model-facing selection and no configured route fields preserve the existing provider path without requiring the optional LLM service.
An enabled definition registers list_subagent_models. With no arguments the tool lists authorized registered providers; with provider it calls that adapter's advisory model catalog only after authorization; with provider and model it authorizes the exact route before resolving its reasoning efforts and default. At most one instance in a tool scope enables selection because the discovery name is global. Shipped product compositions put modelSelectionSettings: true on the primary Agent-scoped subagent instance and register the default-off Host-owned subagent-model-selection setting with an explicit enabled switch and allowedModels list. The Plugins settings page stores both fields atomically. A new top-level Session snapshots the route policy as subagent/model-selection-policy when the setting is enabled, before any model request. A child Session inherits the live parent's policy, and a resumed Session uses its recorded event instead of current settings. Therefore a settings edit affects only subsequently composed top-level Sessions. The fixed discovery definition remains available without the optional LLM service, while discovery and selected-route calls fail until that service is present. Discovery lists the intersection of the live catalog and recorded policy, and the executor rejects explicit routes outside it.
Shipped subagent_fork instances do not read model-selection settings even though the in-process fork provider supports agentOptions. A fork inherits the parent's effective provider and model so its copied conversation prefix remains eligible for provider-side KV Cache reuse. Changing either route component requires the new route to prefill that inherited history again, and that recomputation can dominate the delegated task's cost. This restriction is independent of the discovery tool's global name: separating discovery ownership would permit the configuration but would not preserve reuse. Fork route selection remains unavailable until a route change can retain prefix reuse or the caller can explicitly bound and accept the recomputation cost.
The delegation definition is static across adapter registration and catalog changes, so live topology neither expands every parent request nor invalidates its cache prefix. The discovery result enters the transcript only when called. A custom inheritance-capable instance that enables selection warns that changing provider or model can prevent provider-side reuse of the inherited conversation prefix.
SubagentCapabilities.agentOptions remains the transport truth. The service rejects a request carrying those options before calling a provider that advertises false. Both in-process providers and the DSH SDK transport advertise true; DSH SDK publishes its provider/model defaults as detached immutable data for Consumer preflight, while start() independently applies the same Config defaults plus maxTokens for direct callers and child initialization. ACP, Codex, and Claude Code advertise false. Tool configuration that supplies agentOptions, statically enables model selection, or makes it settings-controlled also fails when its bound provider lacks the capability.
Alternatives considered
Require a deployment-configured route allowlist for static enablement. Rejected because it duplicates the live LLM registry and requires configuration before a custom composition can use an already registered route. The shipped user-owned preference is a distinct authorization decision and is documented by user-authorized subagent model routes.
Render the live adapter catalog in every delegation description. Rejected because one provider can advertise hundreds of models, inflating every request, and catalog changes would rewrite an early cache-prefix definition. The on-demand directory keeps mutable data out of the fixed schema.
Use the advertised catalog as an allowlist. Rejected because adapter catalogs are advisory and some providers accept arbitrary exact model ids. Exact resolution remains authoritative.
Add discovery methods to the subagent service. Rejected because provider/model/effort metadata already belongs to ctx.llm; the new tool is a model-facing consumer of that existing capability.
Export discovery as a separately loaded plugin entry. Rejected because shipped compositions always pair discovery with their primary delegation tool. Explicit ownership on that instance prevents duplicate global names without another Cordis config entry or lifecycle.
Configure discovery independently from model-facing selection. Rejected because discovery exists to supply valid route and effort identifiers to the same model that can select them. One switch prevents a tool schema from advertising selection without its discovery path, or discovery without an applicable delegation route.
Enable model-facing route selection on shipped fork tools. Not shipped because changing provider or model forfeits the inherited prefix's KV Cache reuse and can make prefix recomputation more expensive than the delegated work. The option can be reconsidered when reuse survives the route change or the interface makes that cost explicit and bounded.
Use a global reasoning-effort enum. Rejected because effort identifiers and defaults belong to an exact provider/model route. The LLM adapter validates them without central translation or clamping.
Allow remote providers to ignore the fields. Rejected because the request would claim a route choice that did not happen. The capability flag makes the unsupported path fail before child creation.
Consequences
- A settings-enabled Session can select only its recorded exact child LLM routes; disabled Sessions omit and reject model-facing route fields.
- The primary delegation-tool instance defaults selection off, exposes a Plugins-page exact-route opt-in for new Sessions, and registers
list_subagent_modelsonly in Sessions whose durable policy exists; discovery and explicit selection are constrained to that policy. - Shipped fork tools inherit the parent's provider and model and omit model-facing route fields so the inherited conversation prefix remains eligible for KV Cache reuse.
- Omission retains configured defaults plus static provider route defaults or compatible parent inheritance; a route change without an explicit effort uses the selected model's default.
- Adapter catalog and topology changes leave the delegation definition and its prompt-cache prefix unchanged.
- DSH SDK children accept configured and model-selected Agent routes; ACP, Codex, and Claude Code reject them until they implement and advertise the capability.
- Unit coverage owns the default-off Host preference, new-Session sampling, child inheritance, resumed decisions, opt-in schema and execution enforcement, merge precedence, route-aware effort inheritance, preflight cancellation, live discovery, diagnostics, definition stability, capability rejection, and optional-service behavior. A shipped headless snapshot pins inheritance from a logged parent selection; the shipped examples own the assembled keyless model-visible schemas, and the SDK Loader and snapshot evidence pin the complete route through a separate child runtime.
Related decisions
This note refines only child LLM routing. The fixed subagent transport remains owned by the subagent capability seam, while the separate effect of child-scoped prompt and tool additions on fork prefix reuse remains owned by cache-preserving forked children stay one-shot.
中文
问题
dsh-tool-subagent 可以配置子级 AgentOptions,两个进程内提供方也已把这些值合并到父 Agent 的 LLM 选择之上。但面向模型的工具不能为某个适合的子任务请求不同的提供方、模型或推理强度。为每条 LLM 路由加载一个名称不同的委派工具会重复 schema,并把每次调用的调度选择变成部署配置。
模型还需要一种有界方式来发现实时提供方和模型自有的推理强度 ID。把 adapter 目录渲染到每一份委派描述中,会让仅供参考且会变化的目录进入 prompt 前缀。
决策
只有 Agent 作用域的 modelSelectionSettings 实例解析出 Session 策略,且绑定的 subagent 提供方声明 SubagentCapabilities.agentOptions 时,dsh-tool-subagent 才公开可选的 provider、model 与 reasoning_effort 字段。该策略使用用户授权的 subagent 模型路由所拥有的精确用户授权;不存在无限制静态模式。禁用的实例会省略并拒绝面向模型的选择,而配置的 Config.agentOptions 仍是部署方所有的默认值。如果 settings 已启用的实例缺少该提供方能力,插件挂载会失败。
提供方与模型共同组成一条路由,必须一起提供。如果配置值、父级值或提供方持有的路由默认值能够提供生效路由,则可以只提供推理强度。静态的 provider.agentRouteDefaults 在存在时构成 provider/model 基线;Config.agentOptions 与模型参数会在路由相关强度清除之前覆盖它。没有静态默认值的提供方会使用父 Agent 最新记录请求中的兼容字段,首个请求之前由创建选项提供回退,并保留其中配置的输出 token 上限。推理强度 ID 仍由 adapter 所有。只有所选基线的路由不变时才会继承省略的强度;更换提供方或模型但没有指定强度时,会清除下层路由自有的值,使所选模型解析自己的默认值。AgentOptions 把结果强度传入子级循环,其请求 header 会记录生效值。可继续描述符会把它与解析后的提供方和模型一同记录,使尚未写入首个请求的子级能以相同选择冷恢复。
显式或配置的提供方、模型或强度会在提供方基线与请求优先级完成后,通过 ctx.llm.resolveCallConfig() 解析。具有静态路由默认值的提供方会在请求省略强度时禁止继承父级强度,从而保留所选模型的默认值。LLM 查询负责提供方注册、精确模型元数据、推理强度校验和 adapter 默认值。异步查询完成后、创建子级或后台 job 之前,工具会再次检查取消状态,并确认同一个提供方实例仍处于注册状态,因此 HMR 不会把一个提供方的默认值与另一个提供方的进程组合。既没有面向模型的选择、也没有配置路由字段的调用会保留原有提供方路径,不要求可选 LLM 服务存在。
启用的定义会注册 list_subagent_models。无参数调用列出已授权且已注册的提供方;提供 provider 时先授权,再调用该适配器的建议性模型目录;同时提供 provider 与 model 时会先授权精确路由,再解析其推理强度和默认值。因为发现工具使用全局名称,一个工具作用域最多由一个实例启用选择。随附产品组合在 Agent 作用域的主 subagent 实例上设置 modelSelectionSettings: true,并注册 Host 自有的 subagent-model-selection 设置,其中包含默认关闭的显式 enabled 开关与 allowedModels 列表。Plugins 设置页会原子保存两个字段。设置启用时,新的顶层 Session 会在任何模型请求之前,把路由策略快照记录为 subagent/model-selection-policy。子 Session 继承在线父级的策略;恢复的 Session 使用已记录事件,而不是当前设置。因此,设置修改只影响之后组合的顶层 Session。即使缺少可选 LLM 服务,固定发现定义仍保持可用;发现调用和所选路由调用会在该服务出现前失败。发现会列出实时目录与已记录策略的交集,执行器会拒绝策略之外的显式路由。
随附的 subagent_fork 实例不会读取模型选择设置,即使进程内 fork 提供方支持 agentOptions 也是如此。fork 会继承父级生效的提供方与模型,使复制的对话前缀仍可供提供方侧 KV Cache 复用。更改任一路由组件都会要求新路由重新预填充继承的历史,而这项重算成本可能超过委派任务本身。该限制与发现工具的全局名称无关:分离发现工具的持有权可以让配置生效,却无法保留复用。只有在路由变化仍能保留前缀复用,或调用方可以显式限制并接受重算成本时,才重新考虑 fork 路由选择。
委派定义不会随 adapter 注册和目录变化而改变,因此实时拓扑既不会扩大每个父级请求,也不会使缓存前缀失效。只有调用发现工具时,目录结果才进入 transcript。自定义的上下文继承实例如果启用选择,其描述会警告,更改提供方或模型可能阻止提供方复用继承的对话前缀。
SubagentCapabilities.agentOptions 仍是传输事实。如果请求携带这些选项,而提供方声明为 false,服务会在调用提供方前拒绝。两个进程内提供方与 DSH SDK 传输声明为 true;DSH SDK 会把 provider/model 默认值作为分离且不可变的数据公开给 Consumer 预检,而 start() 会为直接调用方与子运行时初始化独立应用同一份 Config 默认值及 maxTokens。ACP、Codex 与 Claude Code 声明为 false。工具配置提供 agentOptions、静态启用模型选择或让它受 settings 控制时,如果绑定的提供方缺少该能力,也会失败。
考虑过的替代方案
要求静态启用配置部署路由允许列表。 不采用,因为它会重复实时 LLM 注册表,并要求自定义组合先配置才能使用已经注册的路由。随附的用户自有偏好属于另一项授权决定,由用户授权的 subagent 模型路由记录。
在每一份委派描述中渲染实时 adapter 目录。 不采用,因为一个提供方可能公布数百个模型,从而扩大每次请求,而且目录变化会改写缓存前缀中的早期定义。按需目录让可变数据留在固定 schema 之外。
把公布的目录当作允许列表。 不采用,因为 adapter 目录只提供建议,有些提供方接受任意精确模型 ID。精确解析仍是权威。
在 subagent 服务中增加发现方法。 不采用,因为提供方/模型/强度元数据已经属于 ctx.llm;新工具只是该现有能力面向模型的 Consumer。
把发现工具作为独立加载的插件入口导出。 不采用,因为随附组合总是把发现工具与主委派工具配套加载。在该实例上显式指定持有权,可以避免重复的全局工具名,无需增加 Cordis 配置项或独立生命周期。
分别配置发现与面向模型的选择。 不采用,因为发现功能用于向能够选择这些值的同一个模型提供有效的路由与强度 ID。一个开关可以避免工具 schema 公开选择却没有对应发现路径,或公开发现却没有适用的委派路由。
在随附 fork 工具上启用面向模型的路由选择。 不随产品提供,因为更改提供方或模型会失去继承前缀的 KV Cache 复用,重新预填充前缀的成本可能高于委派工作本身。只有在路由变化仍能保留复用,或接口能把这项成本显式化并限制住时,才重新考虑该选项。
使用全局推理强度枚举。 不采用,因为推理强度 ID 和默认值属于精确的提供方/模型路由。LLM adapter 会直接校验,无需中心化翻译或截断。
允许远程提供方忽略这些字段。 不采用,因为请求会声称发生了实际上没有发生的路由选择。能力标记会让不支持的路径在创建子级前失败。
结果
- settings 已启用的 Session 只能选择其记录的精确子级 LLM 路由;禁用的 Session 会省略并拒绝面向模型的路由字段。
- 主委派工具实例默认关闭选择,为新 Session 提供 Plugins 页面精确路由 opt-in,并且只在持久策略存在的 Session 中注册
list_subagent_models;发现与显式选择都受该策略限制。 - 随附 fork 工具会继承父级的提供方与模型,并省略面向模型的路由字段,使继承的对话前缀仍可供 KV Cache 复用。
- 省略选择时保留配置默认值,并使用静态提供方路由默认值或来自父级最新记录请求的兼容继承;改变路由但不显式指定强度时,使用所选模型的默认值。
- adapter 目录和拓扑变化不会改变委派定义及其 prompt 缓存前缀。
- DSH SDK 子级接受配置和模型选择的 Agent 路由;ACP、Codex 与 Claude Code 在实现并声明该能力前仍会拒绝。
- 单元测试覆盖默认关闭的 Host 偏好、新 Session 读取、子级继承、恢复决定、选择启用时的 schema 与执行强制、合并优先级、路由相关强度继承、预检取消、实时发现、诊断、定义稳定性、能力拒绝与可选服务行为。随附的 headless 快照固定从父级已记录选择继承的行为;随附示例覆盖组装后无密钥、模型可见的 schema,SDK Loader 与快照证据固定完整路由经过独立子运行时的链路。
相关决策
本 Note 仅细化子级 LLM 路由。固定的 subagent 传输仍由 subagent 能力 seam负责,而子级作用域提示词与工具增量对 fork 前缀复用产生的独立影响仍由保留缓存的 fork child 保持 one-shot负责。