Merge pull request #33817 from sweetcornna/fix/28503-busy-input-fifo

fix(gateway): use FIFO queue for busy_input_mode pending messages
This commit is contained in:
kshitij
2026-06-08 02:02:02 -07:00
committed by GitHub
3 changed files with 195 additions and 7 deletions
+51 -7
View File
@@ -3427,11 +3427,52 @@ class GatewayRunner(GatewayKanbanWatchersMixin, GatewaySlashCommandsMixin):
except Exception:
return False
# Hard cap on per-session pending follow-ups for busy_input_mode=queue
# (and the draining/steer-fallback/subagent-demotion paths that share
# this entry point). Without a cap, a stuck agent + a rapid-fire user
# could grow the overflow list unboundedly. 32 turns of queued
# follow-ups is far beyond any realistic conversational backlog while
# still small enough to never threaten memory.
_BUSY_QUEUE_MAX_PENDING = 32
def _queue_or_replace_pending_event(self, session_key: str, event: MessageEvent) -> None:
adapter = self.adapters.get(event.source.platform)
if not adapter:
return
merge_pending_message_event(adapter._pending_messages, session_key, event)
# #28503 — Previously this called ``merge_pending_message_event``
# with the default ``merge_text=False``, which silently OVERWROTE
# the single pending slot when consecutive text messages arrived
# in ``busy_input_mode: queue``. Route through the FIFO
# infrastructure shared with ``/queue`` so each follow-up gets
# its own turn in arrival order. Photo bursts still merge into
# the head slot via ``merge_pending_message_event`` (album
# semantics); everything else appends to the overflow tail.
pending_slot = getattr(adapter, "_pending_messages", None)
existing = pending_slot.get(session_key) if isinstance(pending_slot, dict) else None
if existing is not None and (
getattr(existing, "message_type", None) == MessageType.PHOTO
or event.message_type == MessageType.PHOTO
or bool(getattr(existing, "media_urls", None))
or bool(getattr(event, "media_urls", None))
):
# Preserve photo-burst / media-merge semantics for the head slot.
merge_pending_message_event(
adapter._pending_messages,
session_key,
event,
merge_text=event.message_type == MessageType.TEXT,
)
return
if self._queue_depth(session_key, adapter=adapter) >= self._BUSY_QUEUE_MAX_PENDING:
logger.warning(
"Dropping busy-mode follow-up for session %s — pending queue at cap (%d).",
session_key,
self._BUSY_QUEUE_MAX_PENDING,
)
return
self._enqueue_fifo(session_key, event, adapter)
async def _handle_active_session_busy_message(self, event: MessageEvent, session_key: str) -> bool:
# --- Authorization gate (#17775) ---
@@ -7024,12 +7065,15 @@ class GatewayRunner(GatewayKanbanWatchersMixin, GatewaySlashCommandsMixin):
)
adapter = self.adapters.get(source.platform)
if adapter:
merge_pending_message_event(
adapter._pending_messages,
_quick_key,
event,
merge_text=True,
)
if self._busy_input_mode == "queue":
self._enqueue_fifo(_quick_key, event, adapter)
else:
merge_pending_message_event(
adapter._pending_messages,
_quick_key,
event,
merge_text=True,
)
return None
running_agent = self._running_agents.get(_quick_key)