fix(tui): session-owner poller leaves gateway-routed /loop ticks to the gateway
Sibling of the heartbeat fix: a /loop set from a messaging chat carries the gateway's pinned ``route`` (platform + chat_id), and ``gateway/run_goals.py::_loop_wakeup_fire_one`` already defers route-less CLI/TUI loops to their own schedulers. The TUI/Desktop poller never returned the favor, so a Desktop viewer of the same session could fire the wakeup on its own surface and the reply never reached the chat. Mirror the rule: a routed loop is skipped before the session is claimed, so the tick stays due for the gateway scanner. Live probe (temp HERMES_HOME, qqbot-routed loop, Desktop viewer of the same session): before -> loop.ticks=1 awaiting=True (consumed on Desktop); after -> ticks=0, still due. Desktop-owned control loop still fires.
This commit is contained in:
@@ -248,6 +248,14 @@ def _maybe_fire_tui_heartbeat_tick(sid: str, session: dict) -> None:
|
||||
mgr.abandon_fire()
|
||||
|
||||
|
||||
def _loop_route_is_gateway_chat(state) -> bool:
|
||||
"""A /loop set from a messaging chat carries the gateway's ``route`` (platform + chat_id); its wakeup scanner
|
||||
(``gateway/run_goals.py::_loop_wakeup_fire_one``) fires those and skips route-less CLI/TUI loops. Mirror it here
|
||||
so a Desktop viewer of the same session never consumes the tick and strands the reply off the chat."""
|
||||
route = getattr(state, "route", None) or {}
|
||||
return bool(route.get("platform") and route.get("chat_id"))
|
||||
|
||||
|
||||
def _maybe_fire_tui_loop_tick(sid: str, session: dict) -> None:
|
||||
"""Fire a due /loop wakeup for an idle TUI/Desktop/dashboard session (per-session poller, coarse cadence). Claims
|
||||
the session (running=True) before dispatching so a racing user prompt wins; the post-turn hook completes the tick."""
|
||||
@@ -258,7 +266,9 @@ def _maybe_fire_tui_loop_tick(sid: str, session: dict) -> None:
|
||||
if not (sid_key := session.get("session_key") or ""):
|
||||
return
|
||||
mgr = LoopManager(session_id=sid_key)
|
||||
if not mgr.is_due() or goal_blocks_loop_tick(sid_key) or not _notif_claim_turn(session):
|
||||
if not mgr.is_due() or goal_blocks_loop_tick(sid_key) or _loop_route_is_gateway_chat(mgr.state):
|
||||
return # not due, or the gateway's wakeup scanner owns the routed chat — stays due there
|
||||
if not _notif_claim_turn(session):
|
||||
return # busy — stays due, next poll retries
|
||||
if not (wakeup := mgr.fire_tick()):
|
||||
_notif_release_turn(session)
|
||||
|
||||
Reference in New Issue
Block a user