review fix: cover post-cancel replacement
This commit is contained in:
@@ -133,7 +133,7 @@ export async function runLoop(ctx: Context, handle: LoopHandle): Promise<void> {
|
|||||||
if (handle.isDisposed()) break
|
if (handle.isDisposed()) break
|
||||||
|
|
||||||
// Cancellation between wake and `running` skips only the cancelled work;
|
// 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()) {
|
if (handle.isCancelled()) {
|
||||||
handle.clearCancel()
|
handle.clearCancel()
|
||||||
if (!handle.inbox.hasQueued) {
|
if (!handle.inbox.hasQueued) {
|
||||||
|
|||||||
@@ -270,6 +270,31 @@ describe('Agent.cancel()', () => {
|
|||||||
expect(userTexts(agent)).toEqual(['first', 'later'])
|
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 () => {
|
it('cancel() mid-step aborts the active turn and drops every queued tail item', async () => {
|
||||||
const adapter = new MockAdapter(['hang'])
|
const adapter = new MockAdapter(['hang'])
|
||||||
const ctx = await harness(adapter)
|
const ctx = await harness(adapter)
|
||||||
|
|||||||
Reference in New Issue
Block a user