review fix: cover post-cancel replacement

This commit is contained in:
pku-xht
2026-07-20 13:20:51 +08:00
parent da70e47048
commit 7d7d69e225
2 changed files with 26 additions and 1 deletions

View File

@@ -133,7 +133,7 @@ export async function runLoop(ctx: Context, handle: LoopHandle): Promise<void> {
if (handle.isDisposed()) break
// Cancellation between wake and `running` skips only the cancelled work;
// a replacement prompt still runs and owns the eventual idle transition.
// a replacement prompt still runs before the eventual idle transition.
if (handle.isCancelled()) {
handle.clearCancel()
if (!handle.inbox.hasQueued) {

View File

@@ -270,6 +270,31 @@ describe('Agent.cancel()', () => {
expect(userTexts(agent)).toEqual(['first', 'later'])
})
it('replacement work queued after idle-listener cancellation still runs', async () => {
const adapter = new MockAdapter([textResponse('first reply'), textResponse('replacement reply')])
const ctx = await harness(adapter)
const agent = ctx.agentLoop.create(SessionId('idle-listener-post-cancel-send'), { provider: 'mock', model: 'mock' })
const replacementRegistered = Promise.withResolvers<undefined>()
let replacementIdle: Promise<void> | undefined
ctx.on('agent/status', (subject, status) => {
if (subject !== agent || status !== 'idle' || replacementIdle !== undefined) return
send(agent, 'cancelled replacement')
agent.cancel('idle listener')
send(agent, 'surviving replacement')
replacementIdle = agent.whenIdle()
replacementRegistered.resolve(undefined)
})
send(agent, 'first')
await replacementRegistered.promise
if (replacementIdle === undefined) throw new Error('idle listener did not register replacement work')
await replacementIdle
expect(adapter.requests).toHaveLength(2)
expect(userTexts(agent)).toEqual(['first', 'surviving replacement'])
})
it('cancel() mid-step aborts the active turn and drops every queued tail item', async () => {
const adapter = new MockAdapter(['hang'])
const ctx = await harness(adapter)