#14720: fix(slack): pass threadId in plugin read action (#14706)
channel: slack
stale
size: S
trusted-contributor
Cluster:
Slack Thread ID Fixes
## Summary
- **Bug**: Slack plugin's `handleAction` for `action === "read"` silently drops the `threadId` parameter, so thread replies can never be fetched.
- **Root cause**: The `read` branch in `extensions/slack/src/channel.ts` constructs the `handleSlackAction` params without reading `threadId` from the tool params. The core `slack-actions.ts` already supports `threadId` in `readMessages`, but the extension layer never passes it.
- **Fix**: Read `threadId` from params and include it in the `handleSlackAction` call for the `read` action.
Fixes #14706
## Problem
When calling `message(action="read", channel="slack", target="<channel>", threadId="<ts>", limit=N)`, the `threadId` is silently dropped by the Slack plugin extension. The internal `readSlackMessages` function falls through to `conversations.history` (channel-level messages) instead of `conversations.replies` (thread replies).
The bug is in `extensions/slack/src/channel.ts` lines 356-368. The `read` branch reads `limit`, `before`, `after`, and `accountId`, but not `threadId`. Compare with the `send` branch (line 304) which correctly reads `threadId`.
The core code in `src/agents/tools/slack-actions.ts` (line 230) already reads `threadId` and passes it to `readSlackMessages` — the extension layer just never provides it.
**Before fix (reproduced on main via integration test):**
```
pnpm vitest run extensions/slack/src/channel.read-threadid.test.ts
❌ FAIL passes threadId to handleSlackAction when provided
AssertionError: expected undefined to be '1234567890.123456'
```
The test directly calls `slackPlugin.actions.handleAction` with `threadId: "1234567890.123456"` and asserts the param is passed to `handleSlackAction`. On main, it's `undefined`.
## Changes
- `extensions/slack/src/channel.ts` — Read `threadId` from params via `readStringParam` and pass it as `threadId: threadId ?? undefined` to `handleSlackAction` in the `read` branch (+2 lines)
- `extensions/slack/src/channel.read-threadid.test.ts` — New regression test: imports real `slackPlugin`, mocks `getSlackRuntime`, calls `handleAction` with `action="read"` and asserts `threadId` is forwarded
- `CHANGELOG.md` — Add fix entry
**After fix (verified on fix branch):**
```
pnpm vitest run extensions/slack/src/channel.read-threadid.test.ts
✓ passes threadId to handleSlackAction when provided
✓ does not include threadId when not provided
Test Files 1 passed (1)
Tests 2 passed (2)
```
## Test plan
- [x] New test: `threadId` passed to `handleSlackAction` when provided (integration test calling real extension code)
- [x] New test: `threadId` is `undefined` when not provided (no regression)
- [x] All 22 existing slack-actions tests pass (`pnpm vitest run src/agents/tools/slack-actions.test.ts`)
- [x] Lint passes (`pnpm lint`)
- [x] Bug reproduced on main: test fails with `expected undefined to be '1234567890.123456'`
- [x] Fix verified on fix branch: both tests pass
## Effect on User Experience
**Before:** When an agent calls `message(action="read", threadId="...")` on Slack, it always gets channel-level messages instead of thread replies. Users cannot read Slack thread conversations.
**After:** Thread replies are correctly fetched when `threadId` is provided. Channel-level reads (without `threadId`) continue to work as before (no regression).
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR fixes Slack extension message reads for threads by passing the `threadId` tool parameter through the Slack plugin’s `handleAction` read branch (`extensions/slack/src/channel.ts`) into the core Slack action handler (`action: "readMessages"`). Core logic in `src/agents/tools/slack-actions.ts` already supports `threadId` and routes to thread replies when present; this change unblocks that behavior by ensuring the extension layer doesn’t drop the parameter.
Also adds a changelog entry for the user-facing behavior fix.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- The change is a narrow parameter pass-through in the Slack extension layer, and it matches the existing core handler contract (`threadId` is already parsed and forwarded to `readSlackMessages`). No behavioral change occurs when `threadId` is absent, and the added changelog line is straightforward.
- No files require special attention
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#6509: fix(slack): pass threadId param in read action
by morningstar-daemon · 2026-02-01
93.5%
#22216: fix(slack): enable thread reply reading in slack extension plugin
by lan17 · 2026-02-20
92.5%
#5098: fix(slack): forward threadId for message.read
by galligan · 2026-01-31
90.7%
#5514: Slack: fix threadId ignored on read + thread_broadcast dropped
by SanderHelgesen · 2026-01-31
87.9%
#22874: fix(slack): preserve numeric threadId for message read
by HarryMWinters · 2026-02-21
85.9%
#22485: fix(slack): use threadId from delivery context as threadTs fallback...
by dorukardahan · 2026-02-21
84.2%
#12199: fix(message): add threadId parameter to fetch schema for Slack thre...
by dbg-vanie · 2026-02-09
84.0%
#20406: fix(slack): respect replyToMode when computing statusThreadTs in DMs
by QuinnYates · 2026-02-18
83.3%
#4749: fix: handle string thread IDs in queue drain for Slack
by nvonpentz · 2026-01-30
83.0%
#6071: fix(cli): add --thread-id option to message read command
by lyra63237 · 2026-02-01
82.9%