fix: address codex review round 1

- Strict steer now rejects the two windows where an acknowledged
  message would be silently dropped: the closed-turn durability-flush
  window (status still running, loop strands drained steering) and a
  committed structured capture (terminal turn-stop discards late
  steering). Seam JSDoc, catalog doc, README, and the Agent Note
  bilingual pair state the tightened contract; new keyless tests pin
  both rejections.
- Continuable background delegation now fails loud when the advertised
  send_message tool is not registered, instead of starting a durable
  child the model cannot continue. The acp-agent example already loads
  the control tool; the tool-catalog boot recipe is unaffected because
  capability wording is harvested at mount.
This commit is contained in:
Dudu-0223
2026-07-23 17:53:20 +08:00
committed by imccyu
parent 3be7ca8664
commit 71570d7bec
11 changed files with 126 additions and 15 deletions
@@ -259,13 +259,30 @@ function driveTurn(
return handle.dispose()
},
steer(content: ContentBlock[]): void {
// Strict live delivery: the synchronous running check and Agent.steer()
// Strict live delivery: the synchronous checks and the Agent.steer()
// call share one frame, so delivery joins the observed turn or throws.
// Agent.steer()'s own idle fallback would instead QUEUE the message and
// start a new, untracked turn after this run's result was read.
if (child.status !== 'running') {
throw new Error(`subagent child "${childId}" is not running; the message was not delivered`)
}
// The status stays `running` through the closed turn's durability flush,
// and the loop DISCARDS terminal-stopped steering drained after turn
// close instead of recording it. Requiring an open turn keeps
// acknowledged delivery honest.
const lastBoundary = child.session.events.findLast(
event => event.type === 'turn/start' || event.type === 'turn/end',
)
if (lastBoundary?.type !== 'turn/start') {
throw new Error(`subagent child "${childId}" turn has already closed; the message was not delivered`)
}
// A committed structured capture makes the pending `agent/turn-stop`
// checkpoint terminal, and the loop then discards late steering. The
// capture is synchronously observable, so reject rather than
// acknowledge a message the run is about to drop.
if (structured?.captured() !== undefined) {
throw new Error(`subagent child "${childId}" already reported its structured result; the message was not delivered`)
}
child.steer(createUserMessage({ content, source: { kind: 'user' } }))
},
}