Repository navigation
feat(subagent): continue parents after subagents - #56
Conversation
|
367f296 commited by diffex :) |
367f296 to
00943cf
Compare
- Keeps the originating parent prompt active until coordinated child runs terminate - Delivers each frozen batch once through hidden name-sorted context
- Keeps cancellation visible and restores queued editor input before interrupting frozen children - Covers result ordering, retry limits, explicit consumption, and SDK-managed runs
- Restricts coordinated retries to children originally spawned by parent tool calls - Covers failed direct SDK children woken during a parent model turn
- Describes automatic waiting, hidden delivery, retry limits, and cancellation - Documents safe subagent lifecycle events for SDK and RPC consumers
00943cf to
50bc888
Compare
Issacboy123
left a comment
There was a problem hiding this comment.
This PR directionally looks great. The frozen-batch delivery model and the separation lifecycle notices and hidden result context are good. There are just a couple of edge cases for coordination between parent agents and subagents that should be addressed before merging:
- There is an issue with an abort timing window, where cancellation can happen before the subagent wait controller exists. This would lead to possible post-run handling to start a new uncancelled wait, and the child would be a hanging process.
- There are also two state-accounting race conditions around consuming a retry attempt and resetting the retry limit for queued user prompts.
| return true; | ||
| } | ||
|
|
||
| const batch = this._subagentController?.freezeUnconsumedBatch(); |
There was a problem hiding this comment.
If abort() runs after the children spawn but before _waitForSubagentBatch() installs its controller, the parent run is aborted but this code can still freeze the batch and create a fresh, un-aborted wait. A child that never settles would then keep both abort() and the original prompt pending. The cancellation state for the full session run should be preserved, then either initialize this wait as already cancelled or interrupt and skip the pending batch when the parent message was aborted. Aregression where abort lands immediately before post-run handling would help cover the timing window.
| return Object.freeze({ timedOut, agents: Object.freeze(agents) }); | ||
| } | ||
|
|
||
| #consumeTerminalQueryResults(result: SubagentQueryResult): void { |
There was a problem hiding this comment.
This can consume a different attempt from the one represented by result. Since agents and agent_action can execute in parallel, agents may snapshot the failed initial attempt while a concurrent wake replaces record.coordinatedRun with the retry. This method would then mark the new retry consumed, so its eventual result would never be automatically delivered. Include the run ID in the query result and verify it here before changing the delivery state, so only the attempt actually returned by agents is consumed. A regression should run agents({ timeout_ms: 0 }) and the retry action in parallel and verify that the retry result is still delivered.
| } | ||
|
|
||
| preflightResult?.(true); | ||
| this._subagentController?.beginCoordinationCycle(); |
There was a problem hiding this comment.
This advances the coordination cycle for a prompt that starts while idle, but a prompt submitted during streaming takes the queue path without advancing it when delivered. If the previous turn exhausted a child's retry, the queued turn remains in the old cycle and another corrective wake is rejected as a second retry. That conflicts with the documented behavior that a later explicit user prompt starts a fresh cycle. Advance the coordination cycle when each queued explicit user turn is delivered, not only when prompt() begins while idle. Add regression coverage for both queued steering and follow-up input after retry exhaustion.
Resolved by DiffexIssue: "Parent cancellation could occur after child agents spawned but before the subagent wait controller was installed. The cancellation was then lost, allowing a new uncancelled wait to hang the parent and child." Issue: "A terminal Issue: "Queued steering and follow-up user messages did not start a fresh coordination cycle when delivered. A retry exhausted by the preceding turn therefore blocked the queued user's corrective retry." |
Summary
Keep a parent AgentSession active after it delegates work through
spawn_agent.Wait for the frozen child batch, deliver name-sorted results through hidden context, and continue the parent automatically without another user prompt.
Reasoning
Delegation should remain asynchronous for child startup without allowing the enclosing prompt to settle before its coordinated work is usable.
Freezing each batch and tracking per-run delivery gives exactly-once result consumption, deterministic completion behavior, and a clear boundary for cancellation.
Result-free lifecycle notices keep UI and protocol consumers informed without leaking child output into display events or persisted conversation messages.
Changes
Testing
node ../../node_modules/vitest/dist/cli.js --run test/suite/subagent-coordination.test.ts test/interactive-mode-subagent-coordination.test.ts./test.shnpm run check