fix(slack): DM thread attachments stay in thread when sent via message tool#38410
fix(slack): DM thread attachments stay in thread when sent via message tool#38410Taskle wants to merge 110 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes a two-part bug that caused DM thread attachments (TTS audio, images, file uploads sent via the Root cause:
Fix summary:
The fix is minimal, well-targeted, and consistent with the existing asymmetric storage format ( Confidence Score: 5/5
Last reviewed commit: 6194218 |
6194218 to
c28500d
Compare
…ge tool
When an agent is in a Slack DM thread and sends an attachment (image, audio,
file) via the message tool, the attachment was posted outside the thread as a
new top-level message instead of continuing in the existing thread.
Root cause: two gaps in the DM thread context chain:
1. buildSlackThreadingToolContext set currentChannelId = undefined for DMs
because it only extracted the channel ID from 'channel:xxx' prefixed To
values. DM To values use the 'user:xxx' format, so currentChannelId was
always undefined in direct messages.
2. resolveSlackAutoThreadId bailed early if parsedTarget.kind !== 'channel',
which excluded user: targets (kind='user') used for DMs.
Together these meant resolveSlackAutoThreadId always returned undefined in DMs,
so the message tool never injected thread_ts when uploading files — even when
the bot was actively replying in a DM thread.
The auto-reply path (deliverReplies) was unaffected because it uses a separate
replyPlan that correctly applies the isThreadReply override. Only explicit
message tool sends (images, TTS audio, attachments) were broken.
Fix:
- buildSlackThreadingToolContext: preserve 'user:xxx' as currentChannelId for
DM sessions so downstream matching works.
- resolveSlackAutoThreadId: build a canonical target address ('user:xxx' for
DMs, raw channel ID for channels) and compare against currentChannelId,
removing the channel-only kind guard.
Tests: 21 new assertions covering channel threads, DM threads, cross-target
isolation, replyToMode gating, and missing-context guards.
c28500d to
b4ea8cd
Compare
|
Tested on Mattermost (applied custom patch with same root cause fix). Can confirm: the duplicate message delivery issue is fixed ✅. |
…ssion error (openclaw#39435) * fix(setup-podman): cd to TMPDIR before podman load to avoid inherited cwd permission error * fix(podman): safe cwd in run_as_user to prevent chdir errors Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: sallyom <[email protected]> --------- Signed-off-by: sallyom <[email protected]> Co-authored-by: sallyom <[email protected]> Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
…d minimal prompt mode (openclaw#40204) * fix(cron): consolidate announce delivery and detach manual runs * fix: queue detached cron runs (openclaw#40204)
…enclaw#40281) Merged via squash. - Local validation: `pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/server-methods/nodes.invoke-wake.test.ts` - Local validation: `pnpm build` - mb-server validation: `pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/server-methods/nodes.invoke-wake.test.ts` - mb-server validation: `pnpm build` - mb-server validation: `pnpm protocol:check`
…#40282) Merged via squash. - mb-server validation: `swift test --package-path apps/shared/OpenClawKit --filter GatewayNodeSessionTests` - mb-server validation: `pnpm build` - Scope note: top-level `RootTabs` shell change was intentionally removed from this PR before merge
…rmissive hosts (openclaw#39449) * fix(run-openclaw-podman): add SELinux :Z mount option on Linux with enforcing/permissive SELinux * fix(quadlet): add SELinux :Z label to openclaw.container.in volume mount * fix(podman): add SELinux :Z mount option for Fedora/RHEL hosts Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: sallyom <[email protected]> --------- Signed-off-by: sallyom <[email protected]> Co-authored-by: sallyom <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]>
* Docker: shrink runtime image payload * Docker: add runtime pnpm opt-in * Docker: collapse helper entrypoint chmod layers * Docker: restore bundled pnpm runtime * Update CHANGELOG.md
…0342) * docs(changelog): move post-2026.3.8 entries to unreleased * Update CHANGELOG.md
…claw#40345) * fix(tui): improve colour contrast for light-background terminals (openclaw#38636) Detect light terminal backgrounds via COLORFGBG and apply a WCAG AA-compliant light palette. Adds OPENCLAW_THEME=light|dark env var override for terminals without auto-detection. Uses proper sRGB linearisation and WCAG 2.1 contrast ratios to pick whichever text palette (dark or light) has higher contrast against the detected background colour. Co-authored-by: ademczuk <[email protected]> * Update CHANGELOG.md --------- Co-authored-by: ademczuk <[email protected]> Co-authored-by: ademczuk <[email protected]>
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
2 similar comments
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
23 similar comments
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
|
Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch. |
Fixes #38409
What changed
Two files, minimal diff. All existing tests pass; 21 new assertions added.
src/slack/threading-tool-context.tsbuildSlackThreadingToolContextwas discardingcurrentChannelIdfor DMs because it only handledchannel:xxx-prefixedTovalues. DM sessions haveTo = "user:U0AC3LBA08M", socurrentChannelIdwas alwaysundefined.Fix: also preserve
user:xxxaddresses ascurrentChannelIdfor DMs.src/infra/outbound/message-action-params.tsresolveSlackAutoThreadIdexited early whencurrentChannelIdwasundefined(which it always was in DMs — see above), and even if that were fixed it rejecteduser:targets withkind !== "channel".Fix: build a canonical target address (
"user:<id>"for DMs, raw channel ID for channels) and compare againstcurrentChannelId, removing the channel-only kind guard.Why auto-replies were unaffected
deliverRepliesusesreplyPlan.nextThreadTs()which applies theisThreadReply → "all"override directly, never callingresolveSlackAutoThreadId. Only explicitmessagetool sends (TTS audio, images, file uploads) went through the broken path.Test coverage
src/slack/threading-tool-context.test.tssrc/infra/outbound/message-action-params.test.ts(new)