fix(acp): keep per-session teardown failure reasons in the aggregate log

The connection-close teardown path threw a bare `AggregateError` whose
message counts the failed sessions, and its only production consumer logs
through `String(error)` — which renders the message alone. Compared with
the previous `Promise.all` behavior, every actual disposal failure reason
disappeared from operational logs.

Join the per-session reasons into the aggregate message, matching the
subagent seam's own aggregate disposal messages, and pin the reason in
the dispose spec's warning assertion.
This commit is contained in:
Tianyi Cui
2026-08-02 12:23:29 +08:00
parent e81267945a
commit cb835c7ea9
2 changed files with 11 additions and 2 deletions

View File

@@ -368,7 +368,15 @@ export function apply(ctx: Context, config: AcpConfig): void {
if (result.status === 'rejected') failures.push(result.reason as unknown)
}
if (failures.length > 0) {
throw new AggregateError(failures, `ACP agent teardown failed for ${failures.length} session(s)`)
// The only production consumer logs this error through `String`, which
// renders the message alone — without the joined reasons, per-session
// disposal failures would vanish from operational logs. Join them like
// the subagent seam's own aggregate disposal messages.
const detail = failures.map(failure => String(failure)).join('; ')
throw new AggregateError(
failures,
`ACP agent teardown failed for ${failures.length} session(s): ${detail}`,
)
}
})()
return quiescing

View File

@@ -131,7 +131,8 @@ describe('ACP connection ownership', () => {
releaseSecond.resolve(undefined)
await vi.waitFor(() => {
expect(warnings.some(warning => warning.includes('ACP agent teardown failed for 1 session(s)'))).toBe(true)
expect(warnings.some(warning =>
warning.includes('ACP agent teardown failed for 1 session(s): Error: first session cleanup failed'))).toBe(true)
expect(harness!.ctx.agents.get(SessionId(first.sessionId))).toBeUndefined()
expect(harness!.ctx.agents.get(SessionId(second.sessionId))).toBeUndefined()
})