fix(acp): drain continuable subagents before disposing top-level agents

A continuable Activation outlives the turn that started it and owns descendant
teardown, so the bridge must drain that forest child-first before releasing the
top-level agents whose runtime the descendants depend on.

Also rewrites the authored continuable snapshot transcript for the Task-free
tool surface; the scenario's keyless replay is still under diagnosis.
This commit is contained in:
Dudu-0223
2026-07-30 14:41:21 +08:00
committed by Tianyi Cui
parent 3911b7117e
commit c8fbc111db
4 changed files with 154 additions and 85 deletions

View File

@@ -326,10 +326,24 @@ export function apply(ctx: Context, config: AcpConfig): void {
closed = true
const records = [...sessions.values()]
sessions.clear()
quiescing = Promise.all(records.map(async (record) => {
settlePrompt(record, 'cancelled')
await record.dispose()
})).then(() => {})
quiescing = (async () => {
// Continuable subagents outlive the turn that started them, and their
// Activations own descendant teardown. Drain that forest child-first
// BEFORE disposing the top-level agents, so no descendant is left holding
// a runtime its owner already released.
const subagents = ctx.get('subagents')
if (subagents !== undefined) {
try {
await subagents.drainContinuable()
} catch (error: unknown) {
logger.warn(`acp: continuable subagent teardown failed: ${String(error)}`)
}
}
await Promise.all(records.map(async (record) => {
settlePrompt(record, 'cancelled')
await record.dispose()
}))
})()
return quiescing
}