← Back to PRs

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

by dorukardahan open 2026-02-22 06:19 View on GitHub →
channel: slack size: S
# 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: ```ts // 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 - Fixes #5470 - Related: #10837 (Thread replies leak to main channel) - Related: #16080 (Referenced by alternative fix PR #16113) ## 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: ```yaml 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 ```bash 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 - [x] I have read the [CONTRIBUTING.md](../CONTRIBUTING.md) guide - [x] My PR focuses on one thing (respecting replyToMode with auto-created threads) - [x] I have tested my changes locally - [x] I have added/updated tests for my changes - [x] Build, check, and tests all pass - [x] Marked as AI-assisted with testing details <!-- greptile_comment --> <h3>Greptile Summary</h3> 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 <h3>Confidence Score: 3/5</h3> - 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 <sub>Last reviewed commit: f158d38</sub> <!-- greptile_other_comments_section --> <sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub> <!-- /greptile_comment -->

Most Similar PRs