fix: align model selection with current settings remotes

This commit is contained in:
Dudu-0223
2026-08-27 12:18:05 +08:00
parent aad90d5cf3
commit e49e7202c1
13 changed files with 110 additions and 166 deletions
@@ -32,6 +32,7 @@
"dsh": {
"client": {
"inject": [
"@deepseek-ai/dsh-client-connection",
"@deepseek-ai/dsh-client-locale",
"@deepseek-ai/dsh-client-ui-settings",
"@deepseek-ai/dsh-api-remotes"
@@ -47,6 +48,7 @@
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
@@ -55,6 +57,7 @@
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-store": "workspace:^",
"@deepseek-ai/dsh-client-test-runtime": "workspace:^",
@@ -280,7 +280,11 @@ export class SubagentModelSelectionCardController {
this.publish()
await this.scope.mutate([
{ op: 'set', path: ['enabled'], value: desiredEnabled },
{ op: 'set', path: ['allowedModels'], value: desired },
{
op: 'set',
path: ['allowedModels'],
value: desired.map(route => ({ provider: route.provider, model: route.model })),
},
], this.draftRevision)
if (generation !== this.saveGeneration) return
const landed = this.currentEnabled() === desiredEnabled && sameRoutes(this.currentRoutes(), desired)
@@ -188,7 +188,7 @@ describe('SettingsScopeController', () => {
const write = scope.mutate(ops)
ops[0] = { op: 'unset', path: ['enabled'] }
;(ops[1] as { value: Array<{ model: string }> }).value[0]!.model = 'changed'
;(ops[1] as unknown as { value: Array<{ model: string }> }).value[0]!.model = 'changed'
await write
expect(mutate).toHaveBeenCalledWith(
@@ -202,7 +202,7 @@ describe('SettingsScopeController', () => {
})
it('preserves an editor-owned revision fence behind earlier queued writes', async () => {
const first = deferred<RpcResponse<SettingsNamespaceView>>()
const first = deferred<Answer<SettingsNamespaceView>>()
const describeCall = vi.fn()
.mockResolvedValueOnce(described({ preference: 'system' }, 7))
.mockResolvedValueOnce(described({ preference: 'dark' }, 8))
@@ -217,11 +217,12 @@ describe('SettingsScopeController', () => {
first.resolve(ok(view({ preference: 'dark' }, 8)))
await Promise.all([earlier, fenced])
expect(mutate).toHaveBeenNthCalledWith(2, {
ns: 'ui-test',
ops: [{ op: 'set', path: ['preference'], value: 'light' }],
expectedRevision: 7,
})
expect(mutate).toHaveBeenNthCalledWith(
2,
'ui-test',
[{ op: 'set', path: ['preference'], value: 'light' }],
7,
)
expect(scope.getSnapshot()).toMatchObject({ value: { preference: 'dark' }, revision: 8 })
})
@@ -2,7 +2,7 @@ import { Context } from '@deepseek-ai/cordis'
import LlmRuntime, { ToolCallId } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRuntime from '@deepseek-ai/dsh-tools'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { Agent, AgentOptions } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import SubagentRuntime from '@deepseek-ai/dsh-subagent'
@@ -22,14 +22,18 @@ export function fakeAgent(id = 'parent-1'): Agent {
/** Mount the real tool and service stack around one scripted subagent provider. */
const setupAgents = new WeakMap<Context, Agent>()
const setupProviders = new WeakMap<Context, Awaited<ReturnType<typeof mock.mountScriptedProvider>>>()
let setupAgentCounter = 0
/** Test-only opt-in translated to the real Host setting and Session path. */
type SetupConfig = tool.Config & { withModelSelection?: boolean }
type SetupConfig = tool.Config & {
withModelSelection?: boolean
parentAgentOptions?: AgentOptions
}
const TEST_ALLOWED_MODELS = [
'allowed-model', 'configured-model', 'current-model', 'fast-model', 'other-model',
'parent-model', 'unlisted-model',
'allowed-model', 'child-model', 'configured-model', 'current-model', 'fast-model',
'other-model', 'parent-model', 'selected-model', 'unlisted-model',
].flatMap(model => [
{ provider: 'alpha', model },
{ provider: 'current-provider', model },
@@ -38,7 +42,7 @@ const TEST_ALLOWED_MODELS = [
export async function setup(toolConfig: SetupConfig, mockConfig: Partial<mock.Config> = {}): Promise<Context> {
const ctx = new Context()
const { withModelSelection, ...config } = toolConfig
const { withModelSelection, parentAgentOptions, ...config } = toolConfig
if (withModelSelection === true) {
await ctx.plugin(SubagentModelSelectionConfig, {
enabled: true,
@@ -47,9 +51,11 @@ export async function setup(toolConfig: SetupConfig, mockConfig: Partial<mock.Co
await mountAgentLoopTestDependencies(ctx)
await ctx.plugin(AgentLoop, { agents: [] })
await ctx.plugin(SubagentRuntime)
await mock.mountScriptedProvider(ctx, { name: 'mock', ...mockConfig })
const provider = await mock.mountScriptedProvider(ctx, { name: 'mock', ...mockConfig })
setupProviders.set(ctx, provider)
const handle = await ctx.agents.create({
sessionId: SessionId(`model-selection-setup-${++setupAgentCounter}`),
...parentAgentOptions !== undefined ? { agentOptions: parentAgentOptions } : {},
setup: async (agentCtx) => {
await agentCtx.plugin(tool, { ...config, modelSelectionSettings: true })
},
@@ -61,11 +67,20 @@ export async function setup(toolConfig: SetupConfig, mockConfig: Partial<mock.Co
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(SubagentRuntime)
await mock.mountScriptedProvider(ctx, { name: 'mock', ...mockConfig })
const provider = await mock.mountScriptedProvider(ctx, { name: 'mock', ...mockConfig })
setupProviders.set(ctx, provider)
await ctx.plugin(tool, config)
return ctx
}
/** Dispose the scripted provider mounted by {@link setup}. */
export async function disposeSetupProvider(ctx: Context): Promise<void> {
const provider = setupProviders.get(ctx)
if (provider === undefined) throw new Error('context has no setup provider')
setupProviders.delete(ctx)
await provider.dispose()
}
/** Return the real Agent created for a settings-controlled setup. */
export function modelSelectionSetupAgent(ctx: Context): Agent {
const agent = setupAgents.get(ctx)
@@ -238,4 +238,11 @@ describe('list_subagent_models', () => {
expect(text(result)).toContain('available providers: alpha')
expect(text(result)).not.toContain('secret')
})
it('reports no available provider when the authorized registry intersection is empty', async () => {
const ctx = await setupListTool([{ provider: 'missing', model: 'fast' }])
const result = await call(ctx, { provider: 'missing' })
expect(result.isError).toBe(true)
expect(text(result)).toContain('available providers: (none)')
})
})
@@ -2,7 +2,7 @@
import { describe, expect, it, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import { CallId } from '@deepseek-ai/dsh-llm'
import { ToolCallId } from '@deepseek-ai/dsh-llm'
import { Session, SessionId } from '@deepseek-ai/dsh-session'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
import { bindScopeParent, createScope, scopeOf, scopeTarget } from '@deepseek-ai/dsh-scope'
@@ -171,7 +171,7 @@ describe('SubagentModelSelectionConfig', () => {
const result = await ctx.tools.execute({
signal: new AbortController().signal,
callId: CallId('disallowed-session-route'),
callId: ToolCallId('disallowed-session-route'),
name: 'subagent',
arguments: {
description: 'forced route',
@@ -10,7 +10,11 @@ import { Session, SessionId } from '@deepseek-ai/dsh-session'
import { MockAdapter } from '../../../core/agent-loop/tests/mock-adapter.ts'
import * as mock from './scripted-provider.ts'
import * as tool from '../src/index.ts'
import { assertAllowedModelRoutes, assertAllowedModelSelection } from '../src/model-selection.ts'
import {
assertAllowedModelRoutes,
assertAllowedModelSelection,
preflightChildLlmRoute,
} from '../src/model-selection.ts'
import { callSubagent, modelSelectionSetupAgent, setup, text } from './harness.ts'
const REASONING = {
@@ -284,6 +288,12 @@ describe('dsh-tool-subagent model selection', () => {
expect(text(result)).toContain('without an effective provider and model')
})
it('rejects preflight without an effective provider and model', async () => {
const ctx = await setup({ provider: 'mock' })
await expect(preflightChildLlmRoute(ctx.llm, {}, undefined, AbortSignal.abort()))
.rejects.toThrow('without an effective provider and model')
})
it.each([
{ provider: 'alpha' },
{ model: 'fast-model' },
@@ -4,7 +4,7 @@ import { tmpdir } from 'node:os'
import path from 'node:path'
import { Context } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import LlmRuntime, { ToolCallId, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
import { ToolCallId, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRuntime, { TOOL_ABORTED_BEFORE_DISPATCH } from '@deepseek-ai/dsh-tools'
import { assembleContextFor, type Agent } from '@deepseek-ai/dsh-agent'
@@ -21,7 +21,15 @@ import { MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-a
import * as mock from './scripted-provider.ts'
import * as tool from '../src/index.ts'
import { Session, SessionId } from '@deepseek-ai/dsh-session'
import { callSubagent, fakeAgent, setup, testToolSignal, text } from './harness.ts'
import {
callSubagent,
disposeSetupProvider,
fakeAgent,
modelSelectionSetupAgent,
setup,
testToolSignal,
text,
} from './harness.ts'
/**
* Drives the REAL plugin body: mounts `dsh-tool-subagent` on a real
@@ -221,36 +229,19 @@ describe('dsh-tool-subagent', () => {
})
it('merges model overrides over provider-owned route defaults before preflight', async () => {
let seen: { agentOptions?: { provider?: string; model?: string; reasoningEffort?: string; maxTokens?: number } } | undefined
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(SubagentRuntime)
ctx.subagents.registerProvider({
name: 'capture',
capabilities: { agentOptions: true, outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
inheritsParentContext: false,
let seen: SubagentStartRequest | undefined
const ctx = await setup({
provider: 'mock',
withModelSelection: true,
agentOptions: { reasoningEffort: ReasoningEffortId('high'), maxTokens: 321 },
maxDepth: 'provider-managed',
}, {
agentRouteDefaults: { provider: 'alpha', model: 'child-model' },
start: async (request) => {
seen = request
return {
id: SessionId('capture-child'),
localAgent: undefined,
result: Promise.resolve({ output: [{ type: 'text', text: 'ok' }], stopReason: 'completed' as const }),
dispose: async () => {},
}
},
onStart: (request) => { seen = request },
})
ctx.llm.registerAdapter(['alpha'], new MockAdapter([], {
efforts: [{ id: ReasoningEffortId('high'), name: 'High' }],
}))
await ctx.plugin(tool, {
provider: 'capture',
enableModelSelection: true,
agentOptions: { reasoningEffort: ReasoningEffortId('high'), maxTokens: 321 },
maxDepth: 'provider-managed',
})
await callSubagent(ctx, {
description: 'd',
@@ -258,7 +249,7 @@ describe('dsh-tool-subagent', () => {
provider: 'alpha',
model: 'child-model',
})
expect(ctx.tools.schemas().find(schema => schema.name === 'subagent')?.description)
expect(ctx.tools.schemas(modelSelectionSetupAgent(ctx)).find(schema => schema.name === 'subagent')?.description)
.toContain('this provider\'s route defaults')
expect(seen?.agentOptions).toEqual({
provider: 'alpha',
@@ -270,40 +261,21 @@ describe('dsh-tool-subagent', () => {
it('does not inherit parent effort for a provider-owned route default', async () => {
let seen: SubagentStartRequest | undefined
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(SubagentRuntime)
ctx.subagents.registerProvider({
name: 'provider-defaults',
capabilities: { agentOptions: true, outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
inheritsParentContext: false,
agentRouteDefaults: { provider: 'alpha', model: 'child-model' },
start: async (request) => {
seen = request
return {
id: SessionId('provider-default-child'),
localAgent: undefined,
result: Promise.resolve({ output: [{ type: 'text', text: 'ok' }], stopReason: 'completed' as const }),
dispose: async () => {},
}
},
})
ctx.llm.registerAdapter(['alpha'], new MockAdapter([]))
await ctx.plugin(tool, {
provider: 'provider-defaults',
enableModelSelection: true,
maxDepth: 'provider-managed',
})
const parent = {
...fakeAgent('same-route-parent'),
options: {
const ctx = await setup({
provider: 'mock',
withModelSelection: true,
parentAgentOptions: {
provider: 'alpha',
model: 'child-model',
reasoningEffort: ReasoningEffortId('high'),
},
} as Agent
maxDepth: 'provider-managed',
}, {
agentRouteDefaults: { provider: 'alpha', model: 'child-model' },
onStart: (request) => { seen = request },
})
ctx.llm.registerAdapter(['alpha'], new MockAdapter([]))
const parent = modelSelectionSetupAgent(ctx)
const result = await callSubagent(ctx, {
description: 'd',
@@ -312,6 +284,7 @@ describe('dsh-tool-subagent', () => {
model: 'child-model',
}, { agent: parent })
if (result.isError) throw new Error(text(result))
expect(result.isError).toBe(false)
expect(seen?.agentOptions).toEqual({ provider: 'alpha', model: 'child-model' })
})
@@ -994,24 +967,15 @@ describe('dsh-tool-subagent background mode', () => {
})
it('rejects startup when the provider changes during asynchronous route preflight', async () => {
const ctx = new Context()
await ctx.plugin(LlmRuntime)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRuntime)
await ctx.plugin(SubagentRuntime)
const oldStart = vi.fn(async (): Promise<never> => { throw new Error('old provider must not start') })
const oldStart = vi.fn()
const replacementStart = vi.fn(async (): Promise<never> => { throw new Error('replacement provider must not start') })
const disposeOld = ctx.subagents.registerProvider({
name: 'swapped',
capabilities: { agentOptions: true, outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
inheritsParentContext: false,
agentRouteDefaults: { provider: 'alpha', model: 'selected-model' },
start: oldStart,
})
await ctx.plugin(tool, {
provider: 'swapped',
enableModelSelection: true,
const ctx = await setup({
provider: 'mock',
withModelSelection: true,
maxDepth: 'provider-managed',
}, {
agentRouteDefaults: { provider: 'alpha', model: 'selected-model' },
onStart: oldStart,
})
const adapter = new MockAdapter([])
let releasePreflight!: () => void
@@ -1029,9 +993,9 @@ describe('dsh-tool-subagent background mode', () => {
model: 'selected-model',
})
await vi.waitFor(() => { expect(resolveModel).toHaveBeenCalledOnce() })
disposeOld()
await disposeSetupProvider(ctx)
ctx.subagents.registerProvider({
name: 'swapped',
name: 'mock',
capabilities: { agentOptions: true, outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
inheritsParentContext: false,
agentRouteDefaults: { provider: 'beta', model: 'replacement-model' },
+3
View File
@@ -3127,6 +3127,9 @@ importers:
'@deepseek-ai/dsh-api-remotes':
specifier: workspace:^
version: link:../../api/remotes
'@deepseek-ai/dsh-client-connection':
specifier: workspace:^
version: link:../connection
'@deepseek-ai/dsh-client-locale':
specifier: workspace:^
version: link:../locale
@@ -0,0 +1,7 @@
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"mcpCapabilities":{"http":true},"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false},"sessionCapabilities":{"close":{},"list":{},"resume":{}}},"authMethods":[]}}
{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","configOptions":[{"id":"model","name":"Model","category":"model","type":"select","currentValue":"[\"deepseek-official\",\"deepseek-v4-flash\"]","options":[{"group":"deepseek-official","name":"DeepSeek","options":[{"value":"[\"deepseek-official\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek-official\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]}]}]}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_wait","title":"bash","kind":"other","status":"in_progress","rawInput":{"command":"node -e \"const fs=require('node:fs'); fs.writeFileSync('started.tmp', 'started'); fs.renameSync('started.tmp', 'started.txt'); setInterval(() => {}, 1000)\"","description":"Wait until cancellation"}}}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_wait","status":"failed","content":[{"type":"content","content":{"type":"text","text":"Error: tool call aborted"}}]}}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_skipped","title":"bash","kind":"other","status":"in_progress","rawInput":{"command":"printf skipped > skipped.txt","description":"Write skipped marker"}}}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_skipped","status":"failed","content":[{"type":"content","content":{"type":"text","text":"Error: tool call aborted before dispatch"}}]}}}
{"jsonrpc":"2.0","id":3,"result":{"stopReason":"cancelled"}}
@@ -260,23 +260,6 @@
}
}
},
{
"name": "list_subagent_models",
"description": "Discover LLM routes for subagents without changing the current Agent. Call with no arguments to list registered providers, with `provider` to list its advertised models, or with `provider` and `model` to inspect that exact model and its reasoning efforts. Catalog membership is advisory: an adapter may accept an unlisted model id. Use the returned ids with a delegation tool's `provider`, `model`, and `reasoning_effort` fields.",
"parameters": {
"type": "object",
"properties": {
"provider": {
"type": "string",
"description": "Registered LLM provider id. Omit to list providers."
},
"model": {
"type": "string",
"description": "Exact model id to inspect. Requires provider; omit to list that provider's advertised models."
}
}
}
},
{
"name": "ralph",
"description": "Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools.",
@@ -504,7 +487,7 @@
},
{
"name": "subagent",
"description": "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. Give it a complete, standalone prompt: it does not see this conversation. This tool runs in the background by default, immediately returns a durable subagent id, and keeps the child conversation available for later turns. When that run settles, the runtime sends the parent a notice containing its outcome and any final assistant message; `send_message` starts a later turn in the same child conversation. Set `run_in_background: false` only when your next action depends on receiving the result. Child LLM selection is optional. Omit `provider`, `model`, and `reasoning_effort` to use configured child defaults and inherit compatible missing values from the parent Agent. Supply `provider` and `model` together after using `list_subagent_models` to inspect advertised routes and efforts. Changing the effective route without naming an effort uses the selected model's default effort.",
"description": "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. Give it a complete, standalone prompt: it does not see this conversation. This tool runs in the background by default, immediately returns a durable subagent id, and keeps the child conversation available for later turns. When that run settles, the runtime sends the parent a notice containing its outcome and any final assistant message; `send_message` starts a later turn in the same child conversation. Set `run_in_background: false` only when your next action depends on receiving the result.",
"parameters": {
"type": "object",
"properties": {
@@ -516,18 +499,6 @@
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
},
"provider": {
"type": "string",
"description": "LLM provider route for the child. Supply together with model; omit both to use configured child defaults or inherit the parent route."
},
"model": {
"type": "string",
"description": "Model id interpreted by provider. Supply together with provider; omit both to use configured child defaults or inherit the parent route."
},
"reasoning_effort": {
"type": "string",
"description": "Adapter-owned reasoning effort for the effective child route. Omit to inherit a compatible configured/parent effort or use a newly selected model's default."
},
"run_in_background": {
"type": "boolean",
"description": "Whether to run in the background and return a durable subagent id immediately. Defaults to true. Set false to wait for the result when your next action depends on it."
@@ -260,23 +260,6 @@
}
}
},
{
"name": "list_subagent_models",
"description": "Discover LLM routes for subagents without changing the current Agent. Call with no arguments to list registered providers, with `provider` to list its advertised models, or with `provider` and `model` to inspect that exact model and its reasoning efforts. Catalog membership is advisory: an adapter may accept an unlisted model id. Use the returned ids with a delegation tool's `provider`, `model`, and `reasoning_effort` fields.",
"parameters": {
"type": "object",
"properties": {
"provider": {
"type": "string",
"description": "Registered LLM provider id. Omit to list providers."
},
"model": {
"type": "string",
"description": "Exact model id to inspect. Requires provider; omit to list that provider's advertised models."
}
}
}
},
{
"name": "ralph",
"description": "Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools.",
@@ -504,7 +487,7 @@
},
{
"name": "subagent",
"description": "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. Give it a complete, standalone prompt: it does not see this conversation. This tool runs in the background by default, immediately returns a durable subagent id, and keeps the child conversation available for later turns. When that run settles, the runtime sends the parent a notice containing its outcome and any final assistant message; `send_message` starts a later turn in the same child conversation. Set `run_in_background: false` only when your next action depends on receiving the result. Child LLM selection is optional. Omit `provider`, `model`, and `reasoning_effort` to use configured child defaults and inherit compatible missing values from the parent Agent. Supply `provider` and `model` together after using `list_subagent_models` to inspect advertised routes and efforts. Changing the effective route without naming an effort uses the selected model's default effort.",
"description": "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. Give it a complete, standalone prompt: it does not see this conversation. This tool runs in the background by default, immediately returns a durable subagent id, and keeps the child conversation available for later turns. When that run settles, the runtime sends the parent a notice containing its outcome and any final assistant message; `send_message` starts a later turn in the same child conversation. Set `run_in_background: false` only when your next action depends on receiving the result.",
"parameters": {
"type": "object",
"properties": {
@@ -516,18 +499,6 @@
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
},
"provider": {
"type": "string",
"description": "LLM provider route for the child. Supply together with model; omit both to use configured child defaults or inherit the parent route."
},
"model": {
"type": "string",
"description": "Model id interpreted by provider. Supply together with provider; omit both to use configured child defaults or inherit the parent route."
},
"reasoning_effort": {
"type": "string",
"description": "Adapter-owned reasoning effort for the effective child route. Omit to inherit a compatible configured/parent effort or use a newly selected model's default."
},
"run_in_background": {
"type": "boolean",
"description": "Whether to run in the background and return a durable subagent id immediately. Defaults to true. Set false to wait for the result when your next action depends on it."
@@ -1,12 +0,0 @@
version: 1
scenario: subagent-configured-effort-rejection
profile: headless
composition: subagent-configured-effort
recording: authored
header:
class: subagent-configured-effort
pin: true
systemPromptSource: text-turn
toolSchemasSource: text-turn
replay:
override: true