diff --git a/packages/client/ui-settings-plugins/package.json b/packages/client/ui-settings-plugins/package.json index 75b87d26fb..7edd06ff4e 100644 --- a/packages/client/ui-settings-plugins/package.json +++ b/packages/client/ui-settings-plugins/package.json @@ -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:^", diff --git a/packages/client/ui-settings-plugins/src/client/subagent-model-selection-card-controller.ts b/packages/client/ui-settings-plugins/src/client/subagent-model-selection-card-controller.ts index 74917edc56..053074a1ff 100644 --- a/packages/client/ui-settings-plugins/src/client/subagent-model-selection-card-controller.ts +++ b/packages/client/ui-settings-plugins/src/client/subagent-model-selection-card-controller.ts @@ -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) diff --git a/packages/client/ui-settings/tests/settings-scope.client.spec.ts b/packages/client/ui-settings/tests/settings-scope.client.spec.ts index fe0d4013ee..25091897f0 100644 --- a/packages/client/ui-settings/tests/settings-scope.client.spec.ts +++ b/packages/client/ui-settings/tests/settings-scope.client.spec.ts @@ -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>() + const first = deferred>() 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 }) }) diff --git a/packages/subagent/tool-subagent/tests/harness.ts b/packages/subagent/tool-subagent/tests/harness.ts index 6fe8b866e4..91632402d6 100644 --- a/packages/subagent/tool-subagent/tests/harness.ts +++ b/packages/subagent/tool-subagent/tests/harness.ts @@ -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() +const setupProviders = new WeakMap>>() 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 = {}): Promise { 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 { await agentCtx.plugin(tool, { ...config, modelSelectionSettings: true }) }, @@ -61,11 +67,20 @@ export async function setup(toolConfig: SetupConfig, mockConfig: Partial { + 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) diff --git a/packages/subagent/tool-subagent/tests/list-models.spec.ts b/packages/subagent/tool-subagent/tests/list-models.spec.ts index d1739dcce8..308fe47f8e 100644 --- a/packages/subagent/tool-subagent/tests/list-models.spec.ts +++ b/packages/subagent/tool-subagent/tests/list-models.spec.ts @@ -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)') + }) }) diff --git a/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts b/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts index 4078a828b7..ac1a103d11 100644 --- a/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts +++ b/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts @@ -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', diff --git a/packages/subagent/tool-subagent/tests/model-selection.spec.ts b/packages/subagent/tool-subagent/tests/model-selection.spec.ts index 9137e2a9dd..b9565975bb 100644 --- a/packages/subagent/tool-subagent/tests/model-selection.spec.ts +++ b/packages/subagent/tool-subagent/tests/model-selection.spec.ts @@ -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' }, diff --git a/packages/subagent/tool-subagent/tests/tool-subagent.spec.ts b/packages/subagent/tool-subagent/tests/tool-subagent.spec.ts index cff7140a25..27c250a225 100644 --- a/packages/subagent/tool-subagent/tests/tool-subagent.spec.ts +++ b/packages/subagent/tool-subagent/tests/tool-subagent.spec.ts @@ -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 => { throw new Error('old provider must not start') }) + const oldStart = vi.fn() const replacementStart = vi.fn(async (): Promise => { 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' }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f271832de3..0a1cf4046a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/snapshots/acp/cancel-tool-calls/stdout.expected.jsonl b/snapshots/acp/cancel-tool-calls/stdout.expected.jsonl new file mode 100644 index 0000000000..0b49c17e82 --- /dev/null +++ b/snapshots/acp/cancel-tool-calls/stdout.expected.jsonl @@ -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"}} diff --git a/snapshots/session/ralph-loop/tool-schemas.1.expected.json b/snapshots/session/ralph-loop/tool-schemas.1.expected.json index 54d0732db7..dbcc5636e5 100644 --- a/snapshots/session/ralph-loop/tool-schemas.1.expected.json +++ b/snapshots/session/ralph-loop/tool-schemas.1.expected.json @@ -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." diff --git a/snapshots/session/ralph-loop/tool-schemas.2.expected.json b/snapshots/session/ralph-loop/tool-schemas.2.expected.json index 54d0732db7..dbcc5636e5 100644 --- a/snapshots/session/ralph-loop/tool-schemas.2.expected.json +++ b/snapshots/session/ralph-loop/tool-schemas.2.expected.json @@ -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." diff --git a/snapshots/session/subagent-configured-effort-rejection/snapshot.yml b/snapshots/session/subagent-configured-effort-rejection/snapshot.yml deleted file mode 100644 index 327eb8de66..0000000000 --- a/snapshots/session/subagent-configured-effort-rejection/snapshot.yml +++ /dev/null @@ -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