Skip to content

fix(slack): preserve thread parent context when posted by a bot#12561

Closed
Satoshi-agi wants to merge 1 commit into
NousResearch:mainfrom
Satoshi-agi:fix/slack-thread-context-bot-parent
Closed

fix(slack): preserve thread parent context when posted by a bot#12561
Satoshi-agi wants to merge 1 commit into
NousResearch:mainfrom
Satoshi-agi:fix/slack-thread-context-bot-parent

Conversation

@Satoshi-agi

Copy link
Copy Markdown

Problem

When a cron job posts a message to Slack and the user replies in the thread, Hermes previously lost the parent message context and responded with "I don't have the previous context".

Root Cause

  1. gateway/platforms/slack.py::_fetch_thread_context() unconditionally filtered out any message with bot_id, including the thread parent. Cron-posted messages carry bot_id, so the parent was silently dropped and context_parts ended up empty.
  2. Slack's _handle_slack_message() never populated MessageEvent.reply_to_text, so gateway/run.py's shared [Replying to: "..."] injection path (which already works on Telegram/Discord/Feishu/WeCom) was dead code for Slack.

Fix

  1. _fetch_thread_context(): keep bot-posted parents. Only skip our own prior bot replies (circular-context guard) using per-workspace _team_bot_user_ids.get(team) with _bot_user_id fallback — so multi-workspace deployments stay correct. Non-self bot children (e.g. other integrations) are preserved as useful context.
  2. _handle_slack_message(): populate MessageEvent.reply_to_text on thread replies, parity with Telegram/Discord. A new _fetch_thread_parent_text() helper reuses the existing 60s TTL thread cache and only falls back to a cheap conversations.replies(limit=1, inclusive=True) call on cache miss — so no additional API pressure.

Tests (+7 new, 1 updated)

  • test_fetch_thread_context_includes_bot_parent — cron-posted parent surfaces with [thread parent] prefix
  • test_fetch_thread_context_excludes_self_bot_replies — self-bot child excluded, other-bot parent + user children kept
  • test_fetch_thread_context_multi_workspace — per-workspace self-bot identification
  • test_fetch_thread_context_current_ts_excluded — regression guard
  • test_fetch_thread_parent_text_from_cache — cache reuse, no duplicate API call
  • test_slack_reply_to_text_set_on_thread_reply — end-to-end reply_to_text populated for thread-reply DM
  • test_slack_reply_to_text_none_for_top_level_message — top-level DM doesn't set reply_to_text
  • Updated test_skips_bot_messages to match corrected behavior

Test Results

  • Slack-specific suite: 176 passed (was 169)
  • Full suite: same pass/fail counts as main (pre-existing flaky tests untouched)

Impact

Slack adapter only. No changes to session persistence, prompt caching, or other platforms.

…arent

The Slack thread-context fetcher used to drop every message with a
bot_id, which silently erased the thread parent whenever a cron job (or
any other bot) had posted it. As a result, replies to a cron-posted
summary lost all context and the agent answered as if from a blank
thread.

Changes:

1. gateway/platforms/slack.py::_fetch_thread_context
   - Keep the thread parent even when it was posted by a bot
     (e.g. cron summaries, third-party integrations).
   - Only skip *our own* prior bot replies to avoid circular context,
     matching the per-workspace bot user id via _team_bot_user_ids so
     multi-workspace deployments stay correct.
   - Keep non-self bot children (useful third-party context).

2. gateway/platforms/slack.py::_handle_slack_message
   - Populate MessageEvent.reply_to_text for thread replies (parity
     with Telegram/Discord/Feishu/WeCom). gateway.run uses this field
     to inject a [Replying to: "..."] prefix when the parent is not
     already in the session history, which is exactly the scenario
     triggered by cron-generated thread parents.
   - New helper _fetch_thread_parent_text reuses the existing thread-
     context cache (and its 60s TTL) to avoid duplicate
     conversations.replies calls; falls back to a cheap limit=1 fetch
     when the cache is cold.

Tests:

- Updated TestSlackThreadContext::test_skips_bot_messages to reflect
  the new behaviour (self-bot child dropped, third-party bot kept).
- Added:
    * test_fetch_thread_context_includes_bot_parent
    * test_fetch_thread_context_excludes_self_bot_replies
    * test_fetch_thread_context_multi_workspace
    * test_fetch_thread_context_current_ts_excluded (regression guard)
    * test_fetch_thread_parent_text_from_cache
    * test_slack_reply_to_text_set_on_thread_reply
    * test_slack_reply_to_text_none_for_top_level_message

Full Slack suite: 176 passed (was 169).
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists platform/slack Slack app adapter comp/gateway Gateway runner, session dispatch, delivery labels Apr 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Likely duplicate of #12527 — same root cause: _fetch_thread_context() filters all bot messages instead of just self-bot replies.

@teknium1

Copy link
Copy Markdown
Contributor

Merged via #16200 — your commit was cherry-picked onto current main with your authorship preserved (e8b1fdc). Thanks for the thorough fix — the multi-workspace self-bot identification via _team_bot_user_ids.get(team) and the reply_to_text parity with Telegram/Discord were both great additions beyond just the parent-preservation fix.
#16200

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/slack Slack app adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants