fix: address codex review round 1
- Strict steer now rejects the two windows where an acknowledged message would be silently dropped: the closed-turn durability-flush window (status still running, loop strands drained steering) and a committed structured capture (terminal turn-stop discards late steering). Seam JSDoc, catalog doc, README, and the Agent Note bilingual pair state the tightened contract; new keyless tests pin both rejections. - Continuable background delegation now fails loud when the advertised send_message tool is not registered, instead of starting a durable child the model cannot continue. The acp-agent example already loads the control tool; the tool-catalog boot recipe is unaffected because capability wording is harvested at mount.
This commit is contained in:
@@ -285,6 +285,13 @@ export function apply(ctx: Context, config: Config): void {
|
||||
if (control === undefined) {
|
||||
throw new Error('continuable background subagents unavailable: load @deepseek-ai/dsh-subagent-control and @deepseek-ai/dsh-tool-tasks')
|
||||
}
|
||||
// The schema above tells the model to follow up with
|
||||
// `send_message`; starting a durable child the model cannot
|
||||
// continue would make that advertisement false. Sibling load order
|
||||
// is undetermined at mount, so the check lives at the operation.
|
||||
if (ctx.tools.get('send_message') === undefined) {
|
||||
throw new Error('continuable background subagents unavailable: load @deepseek-ai/dsh-tool-subagent-control (the advertised send_message tool is not registered)')
|
||||
}
|
||||
// The control service owns the durable child id, descriptor
|
||||
// snapshot, Task registration, and settle-then-dispose ordering; a
|
||||
// synchronous validation failure rejects the call with no Task.
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
|
||||
import LocalTaskService from '@deepseek-ai/dsh-tasks-local'
|
||||
import SubagentControlService from '@deepseek-ai/dsh-subagent-control'
|
||||
import * as SubagentSpawn from '@deepseek-ai/dsh-subagent-spawn'
|
||||
import * as ToolSubagentControl from '@deepseek-ai/dsh-tool-subagent-control'
|
||||
import * as ToolTasks from '@deepseek-ai/dsh-tool-tasks'
|
||||
import { MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
|
||||
import * as mock from './scripted-provider.ts'
|
||||
@@ -825,7 +826,7 @@ describe('dsh-tool-subagent continuable background mode', () => {
|
||||
})
|
||||
|
||||
/** Boot the real continuable stack: loop, persistence, spawn, tasks, control. */
|
||||
async function continuableSetup() {
|
||||
async function continuableSetup(options: { controlTool?: boolean } = {}) {
|
||||
const ctx = new Context()
|
||||
await mountAgentLoopTestDependencies(ctx)
|
||||
const root = mkdtempSync(path.join(tmpdir(), 'dsh-tool-subagent-continuable-'))
|
||||
@@ -837,6 +838,7 @@ describe('dsh-tool-subagent continuable background mode', () => {
|
||||
await ctx.plugin(LocalTaskService)
|
||||
await ctx.plugin(ToolTasks, {})
|
||||
await ctx.plugin(SubagentControlService)
|
||||
if (options.controlTool !== false) await ctx.plugin(ToolSubagentControl)
|
||||
await ctx.plugin(tool, { provider: 'spawn' })
|
||||
ctx.llm.registerAdapter(['mock'], new MockAdapter([
|
||||
textResponse('continuable answer'),
|
||||
@@ -886,6 +888,21 @@ describe('dsh-tool-subagent continuable background mode', () => {
|
||||
expect(result.isError).toBe(true)
|
||||
expect(text(result)).toContain('load @deepseek-ai/dsh-subagent-control')
|
||||
})
|
||||
|
||||
it('fails loud when the advertised send_message tool is not registered', async () => {
|
||||
// The schema tells the model to follow up with send_message; starting a
|
||||
// durable child the model cannot continue would make that false.
|
||||
const { ctx, parent } = await continuableSetup({ controlTool: false })
|
||||
const result = await callSubagent(
|
||||
ctx,
|
||||
{ description: 'd', prompt: 'p', run_in_background: true },
|
||||
{ agent: parent },
|
||||
)
|
||||
expect(result.isError).toBe(true)
|
||||
expect(text(result)).toContain('load @deepseek-ai/dsh-tool-subagent-control')
|
||||
// Nothing was started: no Task exists for the parent.
|
||||
expect(ctx.tasks.list(parent)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('background preflight failure (no orphaned child, by construction)', () => {
|
||||
|
||||
Reference in New Issue
Block a user