fix: preserve subagent controls across attached session peers

This commit is contained in:
Teknium
2026-09-08 00:08:04 -07:00
parent b3e5c437fc
commit fb5715c19f
4 changed files with 16 additions and 5 deletions

View File

@@ -181,8 +181,12 @@ def test_reattach_preserves_child_controls_including_late_registration(runtime,
popup = type(new)()
with server._session_resume_lock, owner["history_lock"]:
server._rebind_live_transport("ui-owner", owner, popup)
for peer in (new, popup):
assert {r["subagent_id"] for r in call("subagent.list", via=peer)["result"]["subagents"]} == {"before", "after"}
assert call("subagent.tail", via=peer, subagent_id="before")["result"]["text"] == "live child output"
assert server._close_sessions_for_transport(popup) == (0, 0)
assert owner["transport"] is new
assert server._session_transport_contains(owner, new)
assert not server._session_transport_contains(owner, popup)
assert {row["subagent_id"] for row in call("subagent.list", via=new)["result"]["subagents"]} == {"before", "after"}
for sid in ("before", "after"):
assert call("subagent.tail", via=new, subagent_id=sid)["result"]["text"] == "live child output"

View File

@@ -109,6 +109,13 @@ def interrupt_subagent(subagent_id: str) -> bool:
logger.debug("interrupt_subagent(%s) failed: %s", subagent_id, exc)
return False
def _subagent_transport_matches(record, transport) -> bool:
from tui_gateway.transport import FanoutTransport
bound = record.get("owner_transport")
return bound is transport or (isinstance(bound, FanoutTransport) and bound.contains(transport))
def steer_subagent(
subagent_id: str, text: str, *, owner_session_id: Optional[str] = None, owner_transport: Any = None,
owner_session_record: Any = None,
@@ -130,7 +137,7 @@ def steer_subagent(
if owner_session_id is not None and (
record.get("owner_session_id") != owner_session_id
or owner_transport is None
or record.get("owner_transport") is not owner_transport
or not _subagent_transport_matches(record, owner_transport)
or owner_session_record is None
or record.get("owner_session_record") is not owner_session_record
):

View File

@@ -17,12 +17,12 @@ _SUBAGENT_TAIL_BYTES = 16384
def _owned_subagent_records(session_id, transport, owner):
from tools.delegate_tool_registry import _active_subagents, _active_subagents_lock
from tools.delegate_tool_registry import _active_subagents, _active_subagents_lock, _subagent_transport_matches
with _active_subagents_lock:
return [dict(r) for r in _active_subagents.values()
if r.get("owner_session_id") == session_id
and r.get("owner_transport") is transport
and _subagent_transport_matches(r, transport)
and r.get("owner_session_record") is owner]

View File

@@ -476,7 +476,7 @@ def _rebind_live_transport(sid: str, session: dict, transport: Transport) -> Non
if (record.get("owner_session_id") == sid
and record.get("owner_session_record") is session
and record.get("owner_transport") is not None):
record["owner_transport"] = transport
record["owner_transport"] = session["transport"]
# Every transport that showed this session (pop-outs resume the same sid); on disconnect the last
# viewer becomes the transport instead of the drop sentinel.
session.setdefault("viewers", {})[transport] = time.time()