fix(delegation): late-attached children take the parent's soft/hard stop kind; dedupe the fallback replay

_attach_child now mirrors a pending parent stop with the same split
interrupt() uses for its own fan-out (hard -> hard_interrupt, soft ->
interrupt), so a redirect is not turned into a cancel on a child that was
attached late. _restore_parent_cancellation collapses to re-attaching the
rejected unit's children: the replay is the attach step's job now.

Test fixture: _Batch gained origin_session_history_delivery on main after
the salvaged PR was written.

Co-authored-by: illidan <noequal666@gmail.com>
This commit is contained in:
teknium1
2026-09-13 20:19:50 -07:00
committed by Teknium
parent 86258b9912
commit 60559d4e0e
3 changed files with 14 additions and 15 deletions

View File

@@ -375,19 +375,10 @@ def _dispatch_unit(unit: _Batch, unit_id: Optional[str], slot_key: Optional[str]
)
def _restore_parent_cancellation(unit: _Batch) -> None:
# Rejected children remain owned by the parent. Attach before replaying a
# cancellation that may have arrived while async admission had them detached.
parent = unit.parent_agent
"""Rejected children stay owned by the parent: re-attach them (``_attach_child`` replays a stop that
arrived while async admission had them detached)."""
for _, _, child in unit.children:
_attach_child(parent, child)
if getattr(parent, "_interrupt_requested", False) is True:
hard_stop = getattr(parent, "_hard_interrupt_requested", None)
for _, _, child in unit.children:
if hard_stop is not None and hard_stop.is_set():
_signal_child_stop(child, getattr(parent, "_interrupt_message", None))
else:
with _quiet("Failed to propagate interrupt to fallback child: %s"):
child.interrupt(getattr(parent, "_interrupt_message", None))
_attach_child(unit.parent_agent, child)
def _dispatch_background(batch: _Batch) -> str:
"""Dispatch the call as independent async units (see ``_units_of``) and return the tool result JSON. Every unit