fix(subagent): state when to query child routes and when to omit them

The discovery tool presented itself as the place to inspect options before
selecting a child route, and rejected every provider or model outside the
Session's authorized routes, including providers that do not exist. A model
that skipped the no-argument call therefore had no way to learn the correct
ids or that omission was legal, and repeated identical denials.

Say that queries exist to override the child route, that omitting both route
fields inherits the parent route, that a field is omitted rather than sent
empty, and, in each optional route field, that omitting it inherits the
parent value.
This commit is contained in:
Dudu-0223 committed 2026-11-02 15:05:53 +08:00
1 parent 73090b5e56
commit 7301fa98b0
15 files changed
+50 -49

No files matched your search

+1 -1
View File
@@ -246,7 +246,7 @@
en: 841abe0dd5913ea7
zh: 841abe0dd5913ea7
/tool-schema-catalog/deepseek-ai-dsh-tool-subagent/list-subagent-models:
en: 207228068bc61885
en: 2f6d3f253af50d47
zh: 36e9344ee1af8869
/tool-schema-catalog/deepseek-ai-dsh-tool-subagent/subagent:
en: 430672da3c1a6ca6
+1 -1
View File
@@ -2039,7 +2039,7 @@ The subagent_oar tool selects among configured aliases for external providers. T
### `list_subagent_models`
Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation.
Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string.
```json
{
@@ -69,8 +69,8 @@
en: a4ca31b9cbacda6b
zh: 91ac3e0784a14697
/deepseek-ai-dsh-tool-subagent/model-experience/model-selection-and-discovery/what-the-model-sees:
en: 8e6d560368799559
zh: c379e1693f0ce891
en: 931f00959c645b2b
zh: 9efdef9e7ad6bbc6
/deepseek-ai-dsh-tool-subagent/model-experience/model-selection-and-discovery/token-effect:
en: 3a55d6616c00746b
zh: 4b0ef3dbff2c67ce
+1 -1
View File
@@ -153,7 +153,7 @@ Prefix-stable while provider instances and their configuration are unchanged. Ad
#### What the model sees
The optional `list_subagent_models` definition reads the calling Session’s route policy, falling back to the current parent route when no policy is recorded. Calls reject while the optional `ctx.llm` service is unavailable. With a recorded policy, provider model lists intersect the advertised catalog with the Session’s authorized routes. Without one, they resolve only the current parent model, even if the catalog omits it. The discovery tool’s own description instructs the model to query options before selecting a child route; omitting or filtering out discovery removes that guidance. Exact-model queries accept authorized ids absent from the catalog; unauthorized provider/model lookups fail before adapter access. Catalog or exact-model lookup failures and cancellation reject the call. Execution independently enforces the same policy.
The optional `list_subagent_models` definition reads the calling Session’s route policy, falling back to the current parent route when no policy is recorded. Calls reject while the optional `ctx.llm` service is unavailable. With a recorded policy, provider model lists intersect the advertised catalog with the Session’s authorized routes. Without one, they resolve only the current parent model, even if the catalog omits it. The discovery tool’s own description tells the model to query only when overriding the child route, that delegation without route fields inherits the parent route, and that fields are omitted rather than sent empty; omitting or filtering out discovery removes that guidance. Exact-model queries accept authorized ids absent from the catalog; unauthorized provider/model lookups fail before adapter access. Catalog or exact-model lookup failures and cancellation reject the call. Execution independently enforces the same policy.
#### Token effect
+1 -1
View File
@@ -153,7 +153,7 @@ kind: "package-reference"
#### 模型看到什么
可选的 `list_subagent_models` 定义读取调用 Session 的路由策略,没有已记录策略时使用当前父级路由。可选的 `ctx.llm` 服务不可用时,调用会被拒绝。有已记录策略时,提供方模型列表取公布目录与 Session 授权路由的交集。没有策略时,只解析当前父级模型,即使目录未列出它。发现工具自身的描述要求模型在选择子级路由前查询选项;未挂载或过滤掉发现工具时,这段指导也不会出现。精确模型查询允许已授权但目录未列出的 ID;未授权的提供方/模型查询会在访问适配器前失败。目录或精确模型查询失败以及取消都会使调用被拒绝。执行过程独立强制同一策略。
可选的 `list_subagent_models` 定义读取调用 Session 的路由策略,没有已记录策略时使用当前父级路由。可选的 `ctx.llm` 服务不可用时,调用会被拒绝。有已记录策略时,提供方模型列表取公布目录与 Session 授权路由的交集。没有策略时,只解析当前父级模型,即使目录未列出它。发现工具自身的描述要求模型只在覆盖子级路由时查询,说明不带路由字段的委派会继承父级路由,且字段应省略而不是传空;未挂载或过滤掉发现工具时,这段指导也不会出现。精确模型查询允许已授权但目录未列出的 ID;未授权的提供方/模型查询会在访问适配器前失败。目录或精确模型查询失败以及取消都会使调用被拒绝。执行过程独立强制同一策略。
#### Token 影响
+3 -3
View File
@@ -242,15 +242,15 @@ export async function apply(ctx: Context, config: Config): Promise<void> {
...modelSelectionEnabled ? {
provider: {
type: 'string' as const,
description: 'Child LLM provider id.',
description: 'Child LLM provider id; omit together with `model` to inherit the parent route.',
},
model: {
type: 'string' as const,
description: 'Model id within that provider.',
description: 'Model id within that provider; omit together with `provider` to inherit the parent route.',
},
reasoning_effort: {
type: 'string' as const,
description: 'Model-specific reasoning effort id.',
description: 'Model-specific reasoning effort id; omit to use the model\'s default.',
},
} : {},
},
@@ -109,9 +109,10 @@ export function registerListSubagentModels(
ctx.tools.register(defineTool({
name: 'list_subagent_models',
description:
'Inspect subagent models without changing the current Agent. No arguments: providers; '
+ '`provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. '
+ 'Inspect options here before selecting a child provider and model; use returned ids for delegation.',
'Inspect the subagent routes this Session authorizes. No arguments: authorized providers. '
+ '`provider`: that provider\'s authorized models. `provider` + `model`: that model and its reasoning efforts. '
+ 'Query only to override the child route; delegation without `provider` and `model` inherits the parent route. '
+ 'Omit a field rather than sending an empty string.',
parameters: {
provider: {
type: 'string',
@@ -166,7 +166,7 @@ describe('dsh-tool-subagent model selection', () => {
const ctx = await setup({ provider: 'mock', modelSelection: true })
const agent = modelSelectionSetupAgent(ctx)
const descriptions = () => ctx.tools.schemas(agent).map(schema => schema.description).join('\n')
const guidance = 'Inspect options here before selecting a child provider and model'
const guidance = 'Query only to override the child route'
expect(descriptions()).not.toContain(guidance)
expect(descriptions()).not.toContain('list_subagent_models')
@@ -265,7 +265,7 @@
},
{
"name": "list_subagent_models",
"description": "Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation.",
"description": "Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string.",
"parameters": {
"type": "object",
"properties": {
@@ -377,15 +377,15 @@
},
"provider": {
"type": "string",
"description": "Child LLM provider id."
"description": "Child LLM provider id; omit together with `model` to inherit the parent route."
},
"model": {
"type": "string",
"description": "Model id within that provider."
"description": "Model id within that provider; omit together with `provider` to inherit the parent route."
},
"reasoning_effort": {
"type": "string",
"description": "Model-specific reasoning effort id."
"description": "Model-specific reasoning effort id; omit to use the model's default."
}
},
"required": [
@@ -328,7 +328,7 @@
},
{
"name": "list_subagent_models",
"description": "Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation.",
"description": "Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string.",
"parameters": {
"type": "object",
"properties": {
@@ -661,15 +661,15 @@
},
"provider": {
"type": "string",
"description": "Child LLM provider id."
"description": "Child LLM provider id; omit together with `model` to inherit the parent route."
},
"model": {
"type": "string",
"description": "Model id within that provider."
"description": "Model id within that provider; omit together with `provider` to inherit the parent route."
},
"reasoning_effort": {
"type": "string",
"description": "Model-specific reasoning effort id."
"description": "Model-specific reasoning effort id; omit to use the model's default."
}
},
"required": [
@@ -1290,7 +1290,7 @@
},
{
"name": "list_subagent_models",
"description": "Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation.",
"description": "Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string.",
"parameters": {
"type": "object",
"properties": {
@@ -1655,15 +1655,15 @@
},
"provider": {
"type": "string",
"description": "Child LLM provider id."
"description": "Child LLM provider id; omit together with `model` to inherit the parent route."
},
"model": {
"type": "string",
"description": "Model id within that provider."
"description": "Model id within that provider; omit together with `provider` to inherit the parent route."
},
"reasoning_effort": {
"type": "string",
"description": "Model-specific reasoning effort id."
"description": "Model-specific reasoning effort id; omit to use the model's default."
}
},
"required": [
@@ -328,7 +328,7 @@
},
{
"name": "list_subagent_models",
"description": "Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation.",
"description": "Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string.",
"parameters": {
"type": "object",
"properties": {
@@ -661,15 +661,15 @@
},
"provider": {
"type": "string",
"description": "Child LLM provider id."
"description": "Child LLM provider id; omit together with `model` to inherit the parent route."
},
"model": {
"type": "string",
"description": "Model id within that provider."
"description": "Model id within that provider; omit together with `provider` to inherit the parent route."
},
"reasoning_effort": {
"type": "string",
"description": "Model-specific reasoning effort id."
"description": "Model-specific reasoning effort id; omit to use the model's default."
}
},
"required": [
@@ -154,7 +154,7 @@ interface ToolArgsMap {
} & Record<string, JsonValue>;
/** List your entire subagent tree: the root agent and all reachable continuable descendants, including yourself. Rows carry ids, descriptions, and status; descendant rows carry parent ids and depth from the root. You can message any other agent in the subagent tree listed as running or inactive. running means it is working; inactive means no turn is executing; it does not indicate task completion, success, or failure. While you remain active, you will be notified when a direct child finishes; there is no need to keep checking its status. Use send_message to continue the conversation. */
list_agents: Record<string, JsonValue>;
/** Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation. */
/** Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string. */
list_subagent_models: {
/** Registered LLM provider id. */
provider?: string;
@@ -249,11 +249,11 @@ interface ToolArgsMap {
description: string;
/** The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs. */
prompt: string;
/** Child LLM provider id. */
/** Child LLM provider id; omit together with `model` to inherit the parent route. */
provider?: string;
/** Model id within that provider. */
/** Model id within that provider; omit together with `provider` to inherit the parent route. */
model?: string;
/** Model-specific reasoning effort id. */
/** Model-specific reasoning effort id; omit to use the model's default. */
reasoning_effort?: string;
} & Record<string, JsonValue>;
/** Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. This tool starts an independently managed subagent and immediately returns its id. If you are still online when it finishes, the runtime automatically delivers its completion notice. The notice includes the final answer unless the child sent you a message just before finishing. This backend does not accept follow-up messages. */
@@ -154,7 +154,7 @@ interface ToolArgsMap {
} & Record<string, JsonValue>;
/** List your entire subagent tree: the root agent and all reachable continuable descendants, including yourself. Rows carry ids, descriptions, and status; descendant rows carry parent ids and depth from the root. You can message any other agent in the subagent tree listed as running or inactive. running means it is working; inactive means no turn is executing; it does not indicate task completion, success, or failure. While you remain active, you will be notified when a direct child finishes; there is no need to keep checking its status. Use send_message to continue the conversation. */
list_agents: Record<string, JsonValue>;
/** Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation. */
/** Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string. */
list_subagent_models: {
/** Registered LLM provider id. */
provider?: string;
@@ -266,11 +266,11 @@ interface ToolArgsMap {
description: string;
/** The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs. */
prompt: string;
/** Child LLM provider id. */
/** Child LLM provider id; omit together with `model` to inherit the parent route. */
provider?: string;
/** Model id within that provider. */
/** Model id within that provider; omit together with `provider` to inherit the parent route. */
model?: string;
/** Model-specific reasoning effort id. */
/** Model-specific reasoning effort id; omit to use the model's default. */
reasoning_effort?: string;
} & Record<string, JsonValue>;
/** Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn). Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive its result, not its intermediate steps. This tool starts an independently managed subagent and immediately returns its id. If you are still online when it finishes, the runtime automatically delivers its completion notice. The notice includes the final answer unless the child sent you a message just before finishing. Use `send_message` to steer it while running or continue its conversation after it finishes. */
@@ -328,7 +328,7 @@
},
{
"name": "list_subagent_models",
"description": "Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation.",
"description": "Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string.",
"parameters": {
"type": "object",
"properties": {
@@ -661,15 +661,15 @@
},
"provider": {
"type": "string",
"description": "Child LLM provider id."
"description": "Child LLM provider id; omit together with `model` to inherit the parent route."
},
"model": {
"type": "string",
"description": "Model id within that provider."
"description": "Model id within that provider; omit together with `provider` to inherit the parent route."
},
"reasoning_effort": {
"type": "string",
"description": "Model-specific reasoning effort id."
"description": "Model-specific reasoning effort id; omit to use the model's default."
}
},
"required": [
@@ -1290,7 +1290,7 @@
},
{
"name": "list_subagent_models",
"description": "Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation.",
"description": "Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string.",
"parameters": {
"type": "object",
"properties": {
@@ -1655,15 +1655,15 @@
},
"provider": {
"type": "string",
"description": "Child LLM provider id."
"description": "Child LLM provider id; omit together with `model` to inherit the parent route."
},
"model": {
"type": "string",
"description": "Model id within that provider."
"description": "Model id within that provider; omit together with `provider` to inherit the parent route."
},
"reasoning_effort": {
"type": "string",
"description": "Model-specific reasoning effort id."
"description": "Model-specific reasoning effort id; omit to use the model's default."
}
},
"required": [
@@ -154,7 +154,7 @@ interface ToolArgsMap {
} & Record<string, JsonValue>;
/** List your entire subagent tree: the root agent and all reachable continuable descendants, including yourself. Rows carry ids, descriptions, and status; descendant rows carry parent ids and depth from the root. You can message any other agent in the subagent tree listed as running or inactive. running means it is working; inactive means no turn is executing; it does not indicate task completion, success, or failure. While you remain active, you will be notified when a direct child finishes; there is no need to keep checking its status. Use send_message to continue the conversation. */
list_agents: Record<string, JsonValue>;
/** Inspect subagent models without changing the current Agent. No arguments: providers; `provider`: allowed models; `provider` + `model`: that model and its reasoning efforts. Inspect options here before selecting a child provider and model; use returned ids for delegation. */
/** Inspect the subagent routes this Session authorizes. No arguments: authorized providers. `provider`: that provider's authorized models. `provider` + `model`: that model and its reasoning efforts. Query only to override the child route; delegation without `provider` and `model` inherits the parent route. Omit a field rather than sending an empty string. */
list_subagent_models: {
/** Registered LLM provider id. */
provider?: string;
@@ -249,11 +249,11 @@ interface ToolArgsMap {
description: string;
/** The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs. */
prompt: string;
/** Child LLM provider id. */
/** Child LLM provider id; omit together with `model` to inherit the parent route. */
provider?: string;
/** Model id within that provider. */
/** Model id within that provider; omit together with `provider` to inherit the parent route. */
model?: string;
/** Model-specific reasoning effort id. */
/** Model-specific reasoning effort id; omit to use the model's default. */
reasoning_effort?: string;
} & Record<string, JsonValue>;
/** Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn). Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive its result, not its intermediate steps. This tool starts an independently managed subagent and immediately returns its id. If you are still online when it finishes, the runtime automatically delivers its completion notice. The notice includes the final answer unless the child sent you a message just before finishing. Use `send_message` to steer it while running or continue its conversation after it finishes. */