test(subagent): update service, send_message, and delegation specs
The continuable path has no Task, so send_message reports a queued next turn and continuable delegation returns only the durable child id. Pins that a follow-up queues behind an open turn rather than steering it, and that a non-parent caller is rejected. Also makes startContinuable/followup reject rather than throw synchronously when continuation services are absent, so callers have one failure mode.
This commit is contained in:
@@ -68,7 +68,7 @@ function text(result: { content: { type: string; text?: string }[] }): string {
|
||||
}
|
||||
|
||||
describe('dsh-tool-subagent', () => {
|
||||
it('rejects continuable background policy when the configured provider cannot resume', async () => {
|
||||
it('rejects continuable background policy when the provider cannot prepare continuable children', async () => {
|
||||
let failure: unknown
|
||||
try {
|
||||
await setup({
|
||||
@@ -668,10 +668,10 @@ describe('dsh-tool-subagent background mode', () => {
|
||||
return ctx
|
||||
}
|
||||
|
||||
it('keeps a resumable provider one-shot when backgroundMode selects one-shot', async () => {
|
||||
it('keeps a continuable-capable provider one-shot when backgroundMode selects one-shot', async () => {
|
||||
const ctx = await backgroundSetup({ provider: 'mock' })
|
||||
const parent = ownerAgent(ctx, 'sess-parent')
|
||||
let resumeCalls = 0
|
||||
let prepareCalls = 0
|
||||
ctx.subagents.registerProvider({
|
||||
name: 'resumable',
|
||||
capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
|
||||
@@ -685,9 +685,9 @@ describe('dsh-tool-subagent background mode', () => {
|
||||
}),
|
||||
dispose: () => Promise.resolve(),
|
||||
}),
|
||||
resume: async () => {
|
||||
resumeCalls += 1
|
||||
throw new Error('one-shot policy must not resume')
|
||||
prepareContinuable: async () => {
|
||||
prepareCalls += 1
|
||||
throw new Error('one-shot policy must not prepare a continuable child')
|
||||
},
|
||||
})
|
||||
tool.apply(ctx, {
|
||||
@@ -706,7 +706,7 @@ describe('dsh-tool-subagent background mode', () => {
|
||||
})
|
||||
|
||||
expect(text(started)).toBe('started background subagent task subagent-1')
|
||||
expect(resumeCalls).toBe(0)
|
||||
expect(prepareCalls).toBe(0)
|
||||
})
|
||||
|
||||
it('returns a task id immediately and the answer is collected through task_output', async () => {
|
||||
@@ -899,10 +899,13 @@ describe('dsh-tool-subagent continuable background mode', () => {
|
||||
return { ctx, parent }
|
||||
}
|
||||
|
||||
it('starts a continuable child and returns both ids without send_message', async () => {
|
||||
it('starts a continuable child and returns only its durable id, creating no Task', async () => {
|
||||
const { ctx, parent } = await continuableSetup()
|
||||
const schema = ctx.tools.schemas().find(s => s.name === 'subagent')!
|
||||
expect(schema.description).not.toContain('send_message')
|
||||
// Continuable delegation has no Task, so the schema promises no collection.
|
||||
expect(schema.description).not.toContain('task_output')
|
||||
expect(schema.description).not.toContain('task_kill')
|
||||
expect(schema.description).toContain('send_message')
|
||||
|
||||
const started = await callSubagent(
|
||||
ctx,
|
||||
@@ -910,15 +913,19 @@ describe('dsh-tool-subagent continuable background mode', () => {
|
||||
{ agent: parent },
|
||||
)
|
||||
expect(started.isError).toBe(false)
|
||||
const match = /^started subagent (\S+) as task (\S+)$/.exec(text(started))
|
||||
const match = /^started subagent (\S+)$/.exec(text(started))
|
||||
expect(match).not.toBeNull()
|
||||
const [, childId, taskId] = match!
|
||||
const snapshot = await ctx.tasks.wait(taskId as never, 5_000, parent)
|
||||
expect(snapshot.status).toBe('completed')
|
||||
expect(ctx.tasks.read(taskId as never, parent).text).toBe('continuable answer')
|
||||
// The child id names a durable session that outlives the settled Task.
|
||||
const [, childId] = match!
|
||||
// No Task was created for the continuable child.
|
||||
expect(ctx.tasks.list(parent)).toEqual([])
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(ctx.agents.get(SessionId(childId!))).toBeUndefined()
|
||||
}, { timeout: 5_000 })
|
||||
// The child id names a durable session carrying its continuation descriptor.
|
||||
const loaded = await ctx.sessionPersistence.load(SessionId(childId!))
|
||||
expect(loaded.events.some(event => event.type === 'subagent/descriptor')).toBe(true)
|
||||
expect(loaded.events.some(event => event.type === 'assistant/message')).toBe(true)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user