Skip to content

[Slack] Subagent results lose thread_ts in DM assistant threads — cross-thread contamination with concurrent requests #63659

@dev-ithitchhiker

Description

@dev-ithitchhiker

Summary

When Slack's Agents & Assistants feature is enabled, DM messages arrive with thread_ts (assistant thread context). The main agent's first response correctly uses this thread_ts, but subagent results lose the thread context — appearing as new top-level messages or, worse, contaminating unrelated threads when multiple conversations are active simultaneously.

Root Causes

1. replyToMode: "first" consumes threadTs on first reply

createReplyReferencePlanner in reply-threading marks hasReplied=true after the first response. Subsequent responses (subagent results) get undefined threadTs. Even with replyToMode: "all", subagent result turns create new replyPlans without the original incomingThreadTs.

2. removeThreadFromDeliveryContext strips stored threadId

In store session management (store-*.js), when a subagent result updates the session without an explicit threadId, removeThreadFromDeliveryContext() deletes the previously stored thread context from the delivery context.

3. Single-value thread cache causes cross-thread contamination

When using a per-channel cache to preserve thread_ts across turns, concurrent DM conversations (e.g., a deploy request in thread A and a market report in thread B) overwrite each other's cached thread_ts. Results from thread B's subagent end up as replies in thread A.

Proposed Fix

The thread_ts from the originating DM session should be preserved per-session across subagent result turns:

  1. Cache thread_ts per sessionKey (not per channel) on inbound in provider
  2. Restore per-session thread_ts in postSlackMessageBestEffort when outbound threadTs is missing for DM channels
  3. Remove removeThreadFromDeliveryContext call from session delivery context merging — preserve existing threadId when no explicit override is provided
  4. Alternatively, extend createSlackReplyDeliveryPlan to accept a fallback threadTs from the session store

Reproduction

  1. Enable Agents & Assistants in Slack App settings
  2. DM the bot with a task requiring subagent delegation (e.g., deploy via Saya)
  3. First response (delegation status) appears in the assistant thread ✓
  4. Subagent result appears as a new top-level message

Cross-thread contamination:

  1. Open two assistant threads simultaneously in the same DM
  2. Request different tasks in each (e.g., deploy in thread A, market report in thread B)
  3. Thread B's result appears in thread A ✗

Affected Versions

v2026.4.5, v2026.4.7, v2026.4.8 (all npm versions when Slack Agents & Assistants is enabled)

Workaround

Runtime patches on compiled JS files:

  • provider-*.js: Cache inbound thread_ts per sessionKey via globalThis, restore in deliverNormally fallback
  • send-*.js: In postSlackMessageBestEffort, check globalThis cache when threadTs is undefined for DM channels (channelId.startsWith("D"))
  • store-*.js: Replace removeThreadFromDeliveryContext(deliveryContextFromSession(existing)) with deliveryContextFromSession(existing) to preserve threadId

Environment

macOS, local gateway mode (non-Docker), Slack Socket Mode

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions