Skip to content

feat(slack): follow threads where bot was previously mentioned#30754

Closed
chilang wants to merge 2 commits intoopenclaw:mainfrom
chilang:feat/slack-follow-mentioned-threads
Closed

feat(slack): follow threads where bot was previously mentioned#30754
chilang wants to merge 2 commits intoopenclaw:mainfrom
chilang:feat/slack-follow-mentioned-threads

Conversation

@chilang
Copy link

@chilang chilang commented Mar 1, 2026

Summary

When channels.slack.thread.followMentionedThreads is enabled, the bot responds to all replies in threads where it has an active session — without requiring @mention in every reply.

Problem

Currently, when a user @mentions the bot in a Slack channel and a threaded conversation starts, the user must @mention the bot in every single reply to keep the conversation going. This is unnatural — in a thread that's clearly a conversation with the bot, having to re-mention it each time adds friction.

Solution

A new opt-in config option:

channels.slack.thread.followMentionedThreads: true  # default: false

When enabled, thread replies are treated as implicit mentions if the bot already has an active session for that thread. This checks both the thread-specific session key and the base (channel) session key, so it works whether the bot was first invoked via @mention in the thread or in the parent message.

Changes

  • Config: Added channels.slack.thread.followMentionedThreads (boolean, default false)
  • Runtime: Before evaluating implicit mentions, checks if the bot has an active session for the current thread via readSessionUpdatedAt
  • Tests: Updated mock contexts in prepare and monitor test suites

Scope

9 files changed, +38 / -1. No breaking changes, fully opt-in, off by default.

When `channels.slack.thread.followMentionedThreads` is enabled (default: false),
the bot responds to all replies in threads where it has an active session —
without requiring @mention in every reply.

This checks both the thread-specific and base (channel) session keys, so it works
whether the bot was first invoked via @mention in the thread or the parent message.

Config: `channels.slack.thread.followMentionedThreads: true`
@openclaw-barnacle openclaw-barnacle bot added channel: slack Channel integration: slack size: XS labels Mar 1, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10afbeb78d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +223 to +224
readSessionUpdatedAt({ storePath, sessionKey }) != null ||
readSessionUpdatedAt({ storePath, sessionKey: baseSessionKey }) != null

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restrict thread follow-up to the current thread session

Using baseSessionKey as a fallback here makes thread-follow behavior channel-wide instead of thread-scoped: baseSessionKey is derived from route.sessionKey and represents the whole channel, so any prior bot interaction in that channel causes threadFollowImplicit to become true for unrelated threads. With followMentionedThreads enabled (especially where mentions are required), the bot will start replying in threads where it was never invoked, which contradicts the intended “follow only threads where the bot was previously mentioned” behavior.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 1, 2026

Greptile Summary

This PR adds a new opt-in config option channels.slack.thread.followMentionedThreads (default: false) that allows the bot to automatically respond to all replies in threads where it previously had an active session, removing the need to @mention the bot on every reply.

The overall approach — plumbing the flag through config, context, and the prepareSlackMessage path — is clean and well-structured. All the config, schema, label, and context changes are straightforward and correct.

Key concern:

  • Over-broad baseSessionKey fallback in prepare.ts (lines 218–226): The fallback that checks readSessionUpdatedAt against baseSessionKey is scoped to (agent, channel) and does not encode the thread timestamp. Once the bot is @mentioned in any top-level message in a channel (creating a baseSessionKey session), threadFollowImplicit will evaluate to true for every subsequent thread reply in that channel — not just the thread that was spawned from the bot's original reply. This means the feature, as implemented, may cause the bot to respond to entirely unrelated threads in any channel where it was ever mentioned at the top level. The thread-specific key check (sessionKey) is correctly scoped, but the baseSessionKey fallback undermines the stated "active session for this thread" intent.

  • Tests cover the updated mock contracts but do not include a case that verifies the bot does not respond to an unrelated thread in a channel where only a top-level baseSessionKey session exists.

Confidence Score: 2/5

  • Not safe to merge without addressing the over-broad baseSessionKey fallback, which can cause the bot to respond to unrelated threads in channels where it was ever mentioned at the top level.
  • The config plumbing, schema, context wiring, and provider changes are all correct. The thread-specific session key check is also correct. However, the baseSessionKey fallback in the core threadFollowImplicit logic is scoped to the entire channel rather than the specific thread, creating a behavioral gap between the documented intent ("threads where the bot has an active session") and the actual runtime behavior (all threads in any channel where the bot was ever mentioned). This is a logic bug in the feature's most critical path and there are no tests covering the failure scenario.
  • src/slack/monitor/message-handler/prepare.ts — the threadFollowImplicit computation (lines 213–227) needs the baseSessionKey fallback to be tightened to ensure it only matches the thread that was directly spawned from the bot's original channel-level mention.

Last reviewed commit: 10afbeb

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +218 to +226
(() => {
const storePath = resolveStorePath(ctx.cfg.session?.store, {
agentId: route.agentId,
});
return (
readSessionUpdatedAt({ storePath, sessionKey }) != null ||
readSessionUpdatedAt({ storePath, sessionKey: baseSessionKey }) != null
);
})(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

baseSessionKey fallback is too broad — matches unrelated threads

Checking for an existing session under baseSessionKey is scoped to (agent, channel) and does not encode the specific thread timestamp. This means once the bot is @mentioned in any top-level channel message and a base-key session is created, the threadFollowImplicit flag will be true for every subsequent thread reply in that channel — even threads the bot was never part of.

Concrete scenario:

  1. Bot is mentioned in a top-level message in channel X → session created under baseSessionKey
  2. An entirely unrelated thread is started later in channel X
  3. A user replies to that unrelated thread → the baseSessionKey session lookup returns a non-null value → threadFollowImplicit = true → bot responds unexpectedly

The PR description says the feature should activate "in threads where it has an active session," but the baseSessionKey fallback activates it for any thread in the channel after any top-level mention.

A tighter approach would be to confirm that the current threadTs corresponds to the channel-level message that originally created the baseSessionKey session (e.g., by storing the originating message ts as part of the session metadata and comparing it here).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/slack/monitor/message-handler/prepare.ts
Line: 218-226

Comment:
**`baseSessionKey` fallback is too broad — matches unrelated threads**

Checking for an existing session under `baseSessionKey` is scoped to `(agent, channel)` and does **not** encode the specific thread timestamp. This means once the bot is `@mentioned` in any top-level channel message and a base-key session is created, the `threadFollowImplicit` flag will be `true` for *every* subsequent thread reply in that channel — even threads the bot was never part of.

Concrete scenario:
1. Bot is mentioned in a top-level message in channel X → session created under `baseSessionKey`
2. An entirely unrelated thread is started later in channel X
3. A user replies to that unrelated thread → the `baseSessionKey` session lookup returns a non-null value → `threadFollowImplicit = true` → bot responds unexpectedly

The PR description says the feature should activate "in threads where it has an active session," but the `baseSessionKey` fallback activates it for *any thread in the channel* after any top-level mention.

A tighter approach would be to confirm that the current `threadTs` corresponds to the channel-level message that originally created the `baseSessionKey` session (e.g., by storing the originating message `ts` as part of the session metadata and comparing it here).

How can I resolve this? If you propose a fix, please make it concise.

Address review feedback: remove the baseSessionKey fallback from
threadFollowImplicit. The base key is scoped to (agent, channel) and
would cause the bot to respond to every thread in a channel where it
was ever mentioned — not just the specific thread with an active session.

Now only checks the thread-specific sessionKey, which correctly encodes
the thread timestamp.

Added regression test: verifies bot does NOT follow an unrelated thread
when only a base channel session exists.
@chilang
Copy link
Author

chilang commented Mar 1, 2026

Addressed the review feedback in fae17a9:

  • Removed the baseSessionKey fallbackthreadFollowImplicit now only checks the thread-specific sessionKey, which correctly encodes the thread timestamp. The base key was scoped to (agent, channel) and would have caused the bot to respond to every thread in a channel where it was ever mentioned.

  • Added regression test — verifies the bot does NOT follow an unrelated thread when only a base channel session exists (followMentionedThreads: true, base session present, but no thread-specific session → prepared is null).

Type-checked clean, lint clean.

@chilang
Copy link
Author

chilang commented Mar 1, 2026

Closing this PR — upstream merged #29165 (bd78a7429) which implements the same thread-follow behavior via an in-memory participation cache (sent-thread-cache.ts). It's always-on (no config toggle) and correctly scoped to (accountId, channelId, threadTs) with a 24h TTL.

Our approach used the session store to check for active thread sessions, while upstream uses a lightweight in-memory cache that records participation when the bot replies. The upstream approach is arguably better (no disk I/O, no stale session false positives).

The only thing our PR added that upstream doesn't have is the opt-in config toggle (followMentionedThreads). If there's demand for disabling this behavior per-workspace, that could be a separate small PR. But for now, the core need is solved.

Thanks to the reviewers for the feedback — the baseSessionKey scoping issue would have been a real bug.

@chilang chilang closed this Mar 1, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fae17a9ba0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const storePath = resolveStorePath(ctx.cfg.session?.store, {
agentId: route.agentId,
});
return readSessionUpdatedAt({ storePath, sessionKey }) != null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle root-mentioned threads when follow mode is enabled

threadFollowImplicit only checks readSessionUpdatedAt for the thread-suffixed sessionKey, but top-level mentions are persisted under the base channel key because resolveThreadSessionKeys appends threadId only for isThreadReply. In a common flow (followMentionedThreads=true, requireMention=true) where a user mentions the bot in a parent channel message and then continues in that thread without another mention, the first thread reply still gets dropped, so the new follow behavior does not work for parent-started thread conversations.

Useful? React with 👍 / 👎.

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

Labels

channel: slack Channel integration: slack size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant