From f5ef7ee9da66cff764296ed9da9c69b70105a90f Mon Sep 17 00:00:00 2001 From: AIalliAI <285906080+AIalliAI@users.noreply.github.com> Date: Wed, 8 Jul 2026 01:47:26 +0000 Subject: [PATCH] fix(tui): prevent ws_orphan_reap from ending gateway-originated sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guard _finalize_session's db.end_session() call against gateway-owned sessions (telegram, bluebubbles, discord, etc.). The TUI is a viewer for these sessions, not the lifecycle owner. Unconditionally ending them in state.db creates a Groundhog Day routing loop: the gateway's #54878 self-heal detects the stale entry, recovers to the parent session, context compression splits back to the reaped child, and the cycle repeats on every inbound message — causing complete conversational context amnesia. Fixes #60609 --- tui_gateway/server.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tui_gateway/server.py b/tui_gateway/server.py index b5fa9857946..fcd29723bd0 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -586,7 +586,23 @@ def _finalize_session(session: dict | None, end_reason: str = "tui_close") -> No try: db = _get_db() if db is not None: - db.end_session(session_id, end_reason) + # Don't end gateway-originated sessions — the gateway owns their + # lifecycle. The TUI is a viewer, not the owner. Ending a + # gateway session in state.db triggers a Groundhog Day routing + # loop: the gateway's #54878 self-heal detects the stale entry, + # recovers to the parent session, context compression splits + # back to the reaped child, and the cycle repeats on every + # inbound message. (#60609) + _GATEWAY_SOURCES = frozenset({ + "bluebubbles", "telegram", "discord", "signal", + "whatsapp", "sms", "slack", "mattermost", + "matrix", "line", "wechat", "facebook", + "imessage", "googlechat", + }) + row = db.get_session(session_id) + source = (row or {}).get("source", "") + if source not in _GATEWAY_SOURCES: + db.end_session(session_id, end_reason) except Exception: pass