fix: address codex review round 3
- Strict steer additionally requires an OPEN STEP: between steps the loop may be awaiting its continuation/turn-stop checkpoints, where pending steering was already folded and a terminal stop discards a later arrival. A message accepted during an open step is drained and recorded at that step's settlement before any terminal decision, so the acknowledged-then-discarded window is closed. New keyless test holds agent/turn-stop open and pins the rejection. - tool-subagent-control README: distinguish synchronous not-delivered errors from started-Task failures (unknown/foreign/descriptor-less ids settle the started Task as failed), and drop the claim that the completion notice carries the child's response.
This commit is contained in:
@@ -258,6 +258,35 @@ describe('startInProcessRun', () => {
|
||||
await run.dispose()
|
||||
})
|
||||
|
||||
it('strict steer rejects the between-steps window where a terminal turn-stop discards steering', async () => {
|
||||
// Hold `agent/turn-stop` open: the step has closed, pending steering was
|
||||
// already folded into the continuation decision, and a terminal stop
|
||||
// would discard a message arriving now — the exact window an
|
||||
// acknowledged delivery would be a lie.
|
||||
const { ctx, parent } = await setup([textResponse('quick')])
|
||||
let releaseStop: (() => void) | undefined
|
||||
ctx.on('agent/turn-stop', (agent) => {
|
||||
if (agent.session.header.parentSession === undefined || releaseStop !== undefined) return undefined
|
||||
return new Promise((resolve) => {
|
||||
releaseStop = () => { resolve(undefined) }
|
||||
})
|
||||
})
|
||||
const run = await startInProcessRun(request(parent), {})
|
||||
const child = ctx.agents.get(run.id)!
|
||||
await new Promise<void>((resolve) => {
|
||||
const timer = setInterval(() => {
|
||||
if (releaseStop !== undefined) { clearInterval(timer); resolve() }
|
||||
}, 5)
|
||||
})
|
||||
expect(child.status).toBe('running')
|
||||
expect(() => { run.steer!([{ type: 'text', text: 'too late for this turn' }]) })
|
||||
.toThrow(/between steps; the message was not delivered/)
|
||||
releaseStop!()
|
||||
await run.result
|
||||
expect(child.session.events.some(event => event.type === 'steering/message')).toBe(false)
|
||||
await run.dispose()
|
||||
})
|
||||
|
||||
it('strict steer rejects the closed-turn flush window where the loop discards steering', async () => {
|
||||
// Hold the turn-end durability flush open: the turn has closed in the log
|
||||
// and status is still `running`, exactly the window where the loop would
|
||||
|
||||
Reference in New Issue
Block a user