From 50dd67c6808fb0c86297298adbf207db9a03a626 Mon Sep 17 00:00:00 2001 From: Honza Stepanovsky Date: Sun, 26 Apr 2026 12:18:59 -0700 Subject: [PATCH] fix(slack): skip _mentioned_threads registration when strict_mention is on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the strict_mention feature so an @mention in strict mode no longer persistently tags the thread as 'mentioned'. Without this, the thread's first mention would permanently auto-trigger the bot on every subsequent message — which is exactly what strict_mention is designed to prevent. Closes the agent-to-agent ack loop hole hhhonzik identified in #14117. Co-authored-by: hhhonzik --- gateway/platforms/slack.py | 7 +++-- tests/gateway/test_slack_mention.py | 45 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/gateway/platforms/slack.py b/gateway/platforms/slack.py index 01cbddddd78..c9b46be23f8 100644 --- a/gateway/platforms/slack.py +++ b/gateway/platforms/slack.py @@ -1157,8 +1157,11 @@ class SlackAdapter(BasePlatformAdapter): if is_mentioned: # Strip the bot mention from the text text = text.replace(f"<@{bot_uid}>", "").strip() - # Register this thread so all future messages auto-trigger the bot - if event_thread_ts: + # Register this thread so all future messages auto-trigger the bot. + # Skipped in strict mode: strict_mention=true bots must be + # re-mentioned every turn, so remembering the thread would + # defeat the feature (and re-enable agent-to-agent ack loops). + if event_thread_ts and not self._slack_strict_mention(): self._mentioned_threads.add(event_thread_ts) if len(self._mentioned_threads) > self._MENTIONED_THREADS_MAX: to_remove = list(self._mentioned_threads)[:self._MENTIONED_THREADS_MAX // 2] diff --git a/tests/gateway/test_slack_mention.py b/tests/gateway/test_slack_mention.py index 3bf838feaf7..8e4eb5a910b 100644 --- a/tests/gateway/test_slack_mention.py +++ b/tests/gateway/test_slack_mention.py @@ -415,3 +415,48 @@ def test_config_bridges_slack_strict_mention(monkeypatch, tmp_path): assert config is not None import os as _os assert _os.environ["SLACK_STRICT_MENTION"] == "true" + + +# --------------------------------------------------------------------------- +# Regression: strict mode must NOT persist mentions into _mentioned_threads +# --------------------------------------------------------------------------- +# Prevents agent-to-agent ack loops — if a strict-mode bot remembered every +# thread it was mentioned in, the next message from the other agent in that +# thread would re-trigger the bot and defeat the entire feature. + +def test_mention_in_strict_mode_does_not_register_thread(): + adapter = _make_adapter(strict_mention=True) + adapter._bot_user_id = "U_BOT" + adapter._mentioned_threads = set() + adapter._MENTIONED_THREADS_MAX = 5000 + + thread_ts = "1700000000.100200" + event_thread_ts = thread_ts # incoming message is inside an existing thread + + # Mirror the handler's @mention + strict-mode guard that protects + # _mentioned_threads.add(). If strict is on, we must skip the add. + text = "<@U_BOT> hello" + is_mentioned = f"<@{adapter._bot_user_id}>" in text + assert is_mentioned + if event_thread_ts and not adapter._slack_strict_mention(): + adapter._mentioned_threads.add(event_thread_ts) + + assert thread_ts not in adapter._mentioned_threads + + +def test_mention_outside_strict_mode_still_registers_thread(): + adapter = _make_adapter(strict_mention=False) + adapter._bot_user_id = "U_BOT" + adapter._mentioned_threads = set() + adapter._MENTIONED_THREADS_MAX = 5000 + + thread_ts = "1700000000.100200" + event_thread_ts = thread_ts + + text = "<@U_BOT> hello" + is_mentioned = f"<@{adapter._bot_user_id}>" in text + assert is_mentioned + if event_thread_ts and not adapter._slack_strict_mention(): + adapter._mentioned_threads.add(event_thread_ts) + + assert thread_ts in adapter._mentioned_threads