fix: complete scoped lifecycle simplification
This commit is contained in:
@@ -340,6 +340,11 @@ export async function startAcpRun(request: SubagentStartRequest, spec: AcpRunSpe
|
||||
cancelSettled.then((): SubagentResult => ({ output: collectOutput(), stopReason: 'aborted' })),
|
||||
])
|
||||
} catch (error: unknown) {
|
||||
// A deterministic cancellation resolves `cancelSettled` before its
|
||||
// best-effort ACP cancel can reject the prompt. This fallback is only for
|
||||
// a process/pipe rejection already queued when the abort event fires; its
|
||||
// first-outcome ordering cannot be forced without a timing-dependent test.
|
||||
/* v8 ignore next */
|
||||
if (flags.cancelled) return { output: collectOutput(), stopReason: 'aborted' }
|
||||
// The seam contract: result resolves (never rejects) on a child-level
|
||||
// failure. Startup failures were already rejected before publication;
|
||||
|
||||
@@ -127,7 +127,9 @@ describe('dsh-subagent-acp', () => {
|
||||
const result = await run.result
|
||||
expect(result.stopReason).toBe('completed')
|
||||
expect(text(result.output)).toBe('hello from acp child')
|
||||
await run.dispose()
|
||||
const disposal = run.dispose()
|
||||
expect(run.dispose()).toBe(disposal)
|
||||
await disposal
|
||||
})
|
||||
|
||||
it('maps a max_tokens stop reason', async () => {
|
||||
@@ -470,6 +472,19 @@ describe('dsh-subagent-acp', () => {
|
||||
await run.dispose()
|
||||
})
|
||||
|
||||
it('logs a flattened child failure through the registered provider', async () => {
|
||||
const ctx = await setup({ MOCK_CRASH_ON_PROMPT: '1' })
|
||||
const warnings: string[] = []
|
||||
ctx.logger.warn = ((message: unknown) => { warnings.push(String(message)) }) as typeof ctx.logger.warn
|
||||
const run = await ctx.subagents.start('acp', request())
|
||||
const result = await run.result
|
||||
expect(result.stopReason).toBe('error')
|
||||
expect(warnings).toEqual([
|
||||
expect.stringContaining('subagent-acp "acp": child run failed (error):'),
|
||||
])
|
||||
await run.dispose()
|
||||
})
|
||||
|
||||
it('resolves error (never rejects) even when the onError sink itself throws', async () => {
|
||||
// onError is a caller-supplied callback boundary: its own exception must be
|
||||
// contained, or it would reject `result` and break the seam's "result never
|
||||
|
||||
Reference in New Issue
Block a user