fix(agents): parent resumes after yielded child handoff#110922
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 223a21dd76
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| onBeforeYield: | ||
| requesterSessionKey && requesterTurnRunId | ||
| ? async () => { | ||
| const { markRequesterTurnYielded } = await import("./subagent-registry.js"); | ||
| markRequesterTurnYielded({ requesterSessionKey, requesterTurnRunId }); |
There was a problem hiding this comment.
Use the completion-owner key when marking yield
When runSessionKey differs from agentSessionKey, this marks the policy/sandbox session instead of the session that owns the child rows. This occurs for default dmScope="main" direct messages: attempt-tool-base-prepare.ts passes the peer-scoped runtime-policy key as agentSessionKey and the main transcript key as runSessionKey, while the spawn immediately above stores completion ownership under runSessionKey. Consequently markRequesterTurnYieldedInRuns finds no rows, and the later yielded settlement rejects those rows because requesterTurnYielded was never set, leaving the parent-stall race unfixed. Derive this callback's requester key from options.runSessionKey ?? options.agentSessionKey.
Useful? React with 👍 / 👎.
|
Merged via squash.
|
Closes #110572
What Problem This Solves
Fixes an issue where a parent agent that yielded after receiving a child completion could remain stalled instead of resuming to send its final answer. The failure was timing-dependent and could also occur while completion delivery was still in progress.
Why This Change Was Made
The registry now records the exact requester attempt for completion-producing children, persists explicit yield intent before aborting that attempt, and retains the child rows until requester settlement. Yielded batches are durably re-armed with generation-guarded wake state so stale delivery work cannot erase a newer wake. Only completions already delivered or in progress when the requester yields request a fresh turn; children that finish later keep the normal single-delivery path, avoiding duplicate wakes. State remains in the existing SQLite payload and does not require a schema-version change.
This is the owner-boundary fix: requester-attempt settlement owns the race, while child delivery and cleanup keep their existing responsibilities. The regression was introduced by #99396; that guard was intentional, but missed the active-requester yield window.
Implementation and review were AI-assisted.
User Impact
Parent agents now reliably resume and deliver their final response after child work finishes, including the narrow race where completion delivery overlaps sessions_yield. Future child completions still produce one wake, not a duplicate.
Release note: Fix parent-agent handoffs stalling after an already-delivered child result when the parent yields.
Evidence