fix(slack): gate inbound turns on a conversational-subtype allowlist

Housekeeping subtypes (channel_join/leave/topic/name/purpose,
convert_to_private/public, pins, deletions) are not a person speaking,
yet _prefilter_inbound only rejected message_changed/message_deleted,
so each of them started a full agent turn in free-response channels.
Replace the denylist with an allowlist: a message passes when subtype
is absent, file_share, thread_broadcast or me_message; everything else
is dropped. Fixes #110778.
This commit is contained in:
liuhao1024
2026-09-14 18:52:04 +08:00
committed by Teknium
parent 6a22abe5ba
commit cac288a0c3
2 changed files with 88 additions and 2 deletions
+15 -2
View File
@@ -4301,8 +4301,21 @@ class SlackAdapter(BasePlatformAdapter):
return None
if await self._drop_bot_sender(event):
return None
# Edits were normalized above so an @mention added by edit can wake the bot once.
if event.get("subtype") == "message_deleted":
# Edits were normalized above so an @mention added by edit can wake the bot once,
# which also means their subtype is gone by the time this check runs.
# Housekeeping subtypes (joins/leaves, topic/name/purpose changes, convert_to_private/
# public, pins, deletions, file comments...) are not a person speaking, so they must
# not start a turn in free-response channels (#110778). Allowlist rather than denylist
# so subtypes Slack adds later are dropped instead of silently readmitted.
# ``file_share`` passes: a human attaching a file is a person speaking, and the
# ``file_shared`` fallback synthesizes exactly this subtype. ``thread_broadcast``
# passes: a human sharing a threaded reply into the channel carries user/text.
# ``me_message`` passes: ``/me`` is a person speaking.
subtype = event.get("subtype")
if subtype not in (None, "", "file_share", "thread_broadcast", "me_message"):
logger.debug(
"[Slack] Dropping non-conversational message subtype=%s in channel %s",
subtype, channel_id)
return None
return event, dedup_team_id, channel_id