Skip to content

fix(slack): respect replyToMode when incomingThreadTs is auto-created#23320

Closed
dorukardahan wants to merge 2 commits intoopenclaw:mainfrom
dorukardahan:fix/slack-reply-to-mode-thread-ts
Closed

fix(slack): respect replyToMode when incomingThreadTs is auto-created#23320
dorukardahan wants to merge 2 commits intoopenclaw:mainfrom
dorukardahan:fix/slack-reply-to-mode-thread-ts

Conversation

@dorukardahan
Copy link
Contributor

@dorukardahan dorukardahan commented Feb 22, 2026

fix(slack): respect replyToMode when Slack auto-creates thread_ts

AI-assisted: This PR was authored with assistance from an AI coding assistant. All changes were manually reviewed and tested locally before submission.

What

Fixes a bug where replyToMode: "off" was ignored when incomingThreadTs exists, causing all bot replies to become thread replies even when configured to post to the main channel.

Why

When Slack's "Agents & AI Apps" feature is enabled (or in certain bot interaction scenarios), Slack auto-creates a thread_ts for channel messages. The existing code in createSlackReplyReferencePlanner() unconditionally forced replyToMode to "all" whenever incomingThreadTs was present:

// Before (buggy)
const effectiveMode = params.incomingThreadTs ? "all" : params.replyToMode;

This meant users who configured replyToModeByChatType.channel: "off" expecting channel-level replies would instead get thread replies when this auto-threading occurred.

The fix distinguishes between:

  • Genuine thread context (user wrote in an existing thread → isThreadReply=true) → stay in thread regardless of replyToMode
  • Auto-created thread_ts (Slack agent feature / channel message) → respect the configured replyToMode

Related Issues

Reproduction Steps

Before this fix:

  1. Enable Slack "Agents & AI Apps" feature in your workspace (or use a bot that triggers auto-thread creation)
  2. Configure replyToModeByChatType.channel: "off" in OpenClaw config
  3. Send a message in a channel (not in a thread)
  4. Observe that the bot replies in a thread instead of the main channel

Expected behavior:

With replyToMode: "off", the bot should reply to the main channel unless the user explicitly sent their message within an existing thread.

Changes

  1. src/slack/monitor/replies.ts: Modified createSlackReplyReferencePlanner() to accept isThreadReply parameter and use it to determine effective mode instead of just checking incomingThreadTs
  2. src/slack/monitor/message-handler/dispatch.ts: Updated to pass isThreadReply context from resolveSlackThreadContext()
  3. src/slack/threading.ts: Exposed isThreadReply in return value
  4. Tests: Added/updated unit tests for the new behavior

Test Plan

Manual Testing

Scenario 1: Channel Message with Auto-Created Thread (Bug Fix)

  • Configure replyToModeByChatType.channel: "off"
  • Enable Slack "Agents & AI Apps" feature
  • Send a message in a channel (not in a thread)
  • Expected: Bot replies in main channel, not in auto-created thread

Scenario 2: Genuine Thread Reply (Preserved Behavior)

  • Configure replyToModeByChatType.channel: "off"
  • Start a thread by clicking "Reply in thread" on any message
  • Send message within that thread
  • Expected: Bot replies within the thread (genuine thread context respected)

Scenario 3: DM with replyToMode

  • Configure replyToModeByChatType.im: "off" (or any value)
  • Send DM to bot
  • Expected: Bot respects configured mode for IM type

Scenario 4: replyToMode: "all" (Preserved Behavior)

  • Configure replyToModeByChatType.channel: "all"
  • Send message in channel (not thread)
  • Expected: Bot replies in thread (existing behavior preserved)

Scenario 5: Mixed Chat Types

  • Configure different modes for different chat types:
    replyToModeByChatType:
      channel: "off"
      group: "all"
      im: "off"
  • Test in channel, group DM, and 1:1 DM
  • Expected: Each chat type respects its own configuration

Automated Tests

  • pnpm test -- src/slack/monitor/replies.test.ts passes
  • New test cases added:
    • should respect replyToMode="off" when incomingThreadTs exists but isThreadReply=false
    • should force thread reply when isThreadReply=true regardless of replyToMode
    • should handle auto-created thread_ts from Slack Agents feature
  • Existing test cases still pass (no regressions)

Edge Cases

  • Empty thread_ts: Message with no thread context works correctly
  • Nested threads: Thread within a thread handles correctly
  • Message edits: Edited messages maintain correct reply context
  • Bot mentions: @bot mentions in channel vs thread behave correctly
  • Channel-wide replyToMode (deprecated but supported): Still works as fallback

Build & Check

pnpm build && pnpm check && pnpm test
  • Build completes without errors
  • Type checking passes (pnpm check)
  • All tests pass (pnpm test)
  • Lint passes (pnpm lint if applicable)

Comparison with PR #16113

PR #16113 (by @zerone0x) addresses a similar issue but at the deliverReplies() level, focusing on inline directive tags (e.g., @openclaw reply-to=channel). This approach:

  • Only fixes message delivery, not the planning phase
  • Doesn't address the root cause in createSlackReplyReferencePlanner()
  • Has been stale for some time

Our approach is more comprehensive:

  • Fixes the planner level (createSlackReplyReferencePlanner()) where reply mode decisions are made
  • Also ensures deliverReplies() correctly handles the replyToId parameter
  • Handles both config-based and inline directive-based scenarios
  • Includes proper test coverage for the planner logic

Checklist

  • I have read the CONTRIBUTING.md guide
  • My PR focuses on one thing (respecting replyToMode with auto-created threads)
  • I have tested my changes locally
  • I have added/updated tests for my changes
  • Build, check, and tests all pass
  • Marked as AI-assisted with testing details

Greptile Summary

fixes replyToMode: "off" being ignored when Slack auto-creates thread_ts for channel messages (Agents & AI Apps feature)

Key changes:

  • adds isThreadReply flag to distinguish genuine thread replies from auto-created thread_ts
  • isThreadReply is true when thread_ts !== ts OR parent_user_id is present
  • updates createSlackReplyReferencePlanner to respect configured replyToMode for auto-created threads
  • adds test coverage for auto-created thread scenarios

Issues found:

  • logic bug in resolveSlackThreadTargets (src/slack/threading.ts:45-49) - doesn't handle replyToMode: "first" correctly for auto-created threads
  • missing test coverage for "first" mode with auto-created threads

Confidence Score: 3/5

  • safe to merge after fixing the logic bug in resolveSlackThreadTargets
  • the PR correctly fixes the core issue in createSlackReplyReferencePlanner, but introduces a logic bug in resolveSlackThreadTargets that doesn't properly handle replyToMode: "first" with auto-created threads - this needs to be fixed before merge
  • src/slack/threading.ts requires fixing the ternary logic on lines 45-49 to handle "first" mode correctly

Last reviewed commit: f158d38

(5/5) You can turn off certain types of comments like style here!

When Slack's 'Agents & AI Apps' feature is enabled (or in certain bot
interaction scenarios), Slack auto-creates a thread_ts for channel
messages. The existing code in createSlackReplyReferencePlanner()
unconditionally forced replyToMode to 'all' whenever incomingThreadTs
was present, ignoring the user's configured replyToMode.

The fix distinguishes between genuine thread replies (where
incomingThreadTs differs from messageTs, indicating the user wrote in
an existing thread) and auto-created thread_ts values. Only genuine
thread replies force 'all' mode; otherwise the configured replyToMode
is respected.

Changes:
- replies.ts: Accept isThreadReply param, use it instead of bare
  incomingThreadTs presence check
- threading.ts: resolveSlackThreadTargets now checks isThreadReply
  before using incomingThreadTs for reply threading
- dispatch.ts: Compute and pass isThreadReply to delivery plan
- threading.test.ts: Add tests for auto-created thread_ts scenarios

Fixes openclaw#5470
@openclaw-barnacle openclaw-barnacle bot added channel: slack Channel integration: slack size: S labels Feb 22, 2026
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.

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +45 to +49
const replyThreadTs = isThreadReply
? incomingThreadTs
: params.replyToMode === "off"
? undefined
: (incomingThreadTs ?? (params.replyToMode === "all" ? messageTs : undefined));
Copy link
Contributor

Choose a reason for hiding this comment

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

logic bug: replyToMode: "first" not handled correctly for auto-created threads

when isThreadReply=false (auto-created thread) and replyToMode="first", the ternary evaluates to incomingThreadTs ?? undefined, which returns the auto-created thread_ts instead of messageTs for the first reply

Suggested change
const replyThreadTs = isThreadReply
? incomingThreadTs
: params.replyToMode === "off"
? undefined
: (incomingThreadTs ?? (params.replyToMode === "all" ? messageTs : undefined));
const replyThreadTs = isThreadReply
? incomingThreadTs
: params.replyToMode === "off"
? undefined
: params.replyToMode === "all"
? (incomingThreadTs ?? messageTs)
: (params.replyToMode === "first" ? messageTs : undefined);
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/slack/threading.ts
Line: 45-49

Comment:
logic bug: `replyToMode: "first"` not handled correctly for auto-created threads

when `isThreadReply=false` (auto-created thread) and `replyToMode="first"`, the ternary evaluates to `incomingThreadTs ?? undefined`, which returns the auto-created thread_ts instead of `messageTs` for the first reply

```suggestion
  const replyThreadTs = isThreadReply
    ? incomingThreadTs
    : params.replyToMode === "off"
      ? undefined
      : params.replyToMode === "all"
        ? (incomingThreadTs ?? messageTs)
        : (params.replyToMode === "first" ? messageTs : undefined);
```

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

expect(context.isThreadReply).toBe(false);
expect(context.messageThreadId).toBeUndefined();
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

add test for replyToMode: "first" with auto-created thread_ts to verify first reply uses messageTs when thread_ts equals ts

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/slack/threading.test.ts
Line: 126

Comment:
add test for `replyToMode: "first"` with auto-created `thread_ts` to verify first reply uses `messageTs` when `thread_ts` equals `ts`

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

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

…eads

- Fix logic bug in resolveSlackThreadTargets where 'first' mode with
  auto-created thread_ts would use incomingThreadTs instead of messageTs
- Add test for replyToMode 'first' with auto-created thread_ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@vincentkoc
Copy link
Member

Consolidating this into #23839 so we ship one scoped Slack threading fix:

  • honor replyToMode for Slack auto-created top-level thread_ts
  • keep real thread replies threaded
  • avoid behavior regressions around first/all

Closing this as superseded.
EOF && gh pr close 23320 --repo openclaw/openclaw --comment "Superseded by #23839."

@vincentkoc
Copy link
Member

Superseded by #23839.

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.

[Bug]: Slack replyToMode "off" creates threads in channels despite configuration (2026.1.29)

2 participants