Skip to content

Slack: each outbound reply persists a new single-message delivery-mirror session instead of reusing the conversation's thread session #96535

Description

@gorkem2020

Summary

On Slack, when an agent posts a reply, the outbound delivery-mirror persists a brand-new session record keyed agent:<id>:slack:channel:<cid>:thread:<ts> for that reply, rather than appending to the conversation's existing thread session. Each reply carries a distinct timestamp, so a steady stream of single-message sessions accumulates: each contains only the bot's outgoing message (a session header plus one assistant delivery-mirror message, with no inbound user turn), or an empty transcript. An active agent accumulates dozens of these near-empty session records.

A secondary observation: the minted mirror entry has no spawnedCwd, so the session transcript header cwd can fall back to process.cwd() (the gateway process working directory) rather than the agent workspace directory.

Environment

  • OpenClaw 2026.6.1, host-native Node 24 install, gateway running dist/index.js gateway.
  • @openclaw/slack channel plugin 2026.6.1.
  • Still present on the latest release v2026.6.10: no functional change to the outbound mirror session path landed in 2026.6.5 through 2026.6.10.

Steps to reproduce

  1. Configure a Slack channel with an agent that replies in threads (normal threaded behavior).
  2. Have the agent post several replies on the channel.
  3. Inspect the agent session store (sessions.json) and the *.jsonl transcripts.

Expected vs actual

  • Expected: an outbound reply in a thread is mirrored into that thread's existing session (:thread:<rootTs>), so one thread maps to one session.
  • Actual: each outbound reply persists a separate session keyed :thread:<ts> containing only the bot's message, so a single conversation accumulates many assistant-only session records.

Evidence from a live deployment

  • Across the fleet, dozens of :thread:<ts> session entries appear only for Slack-active agents; on one channel a single agent accumulated 21 such entries.
  • The large majority are single-message records: a header plus one assistant message tagged model: "delivery-mirror" with an idempotency key of the form <...>:message-tool:call_<...>, and no inbound user turn; some have an empty transcript.
  • The suffixes are distinct, tightly spaced timestamps (seconds apart) within a continuous exchange. One minted session's outbound reply timestamp was observed becoming the :thread: suffix of the next minted session.
  • One minted key carried thread:/dev/null as its thread id, indicating a non-timestamp value reached the session-key suffix without validation.

Pointers for triage (verified present in the deployed build, not a claimed root cause)

Filenames are build-hashed esbuild chunk names; line numbers are from the deployed 2026.6.1 build.

  • The outbound delivery-mirror is rebuilt and persisted on every send: prepareOutboundMirrorRoute calls ensureOutboundSessionEntry and writes the mirror under the resolved route's session key (dist/message-action-runner-<hash>.js, around lines 565-590).
  • ensureOutboundSessionEntry calls recordSessionMetaFromInbound with no createIfMissing argument (dist/outbound-session-<hash>.js, around lines 122-141), and that defaults createIfMissing = params.createIfMissing ?? true (dist/store-<hash>.js:1254; the only skip is if (!existing && !createIfMissing) at line 1283), so a route key with no existing entry mints a new session.
  • The thread-suffixed key is built by resolveThreadSessionKeys as `${baseSessionKey}:thread:${normalizedThread}` (dist/session-key-<hash>.js, around lines 182-192).
  • The cwd fallback: the transcript header sets cwd: params.cwd ?? process.cwd() (dist/transcript-<hash>.js, around line 142); the mirror append passes cwd: entry.spawnedCwd (around line 745), which is absent on the mirror entry.

Related (not duplicate)

  • #80091 (Slack DM delivery mirrors split sessions; its fix PR #80111 was closed without merging). Same delivery-mirror routing area, but that report is a DM peer mis-classification.
  • #78505 (outbound thread reply does not promote the session key; closed). Closest conceptual match.
  • #87019 (Slack DM session fan-out; open) and umbrella #69208 (cross-channel transcript and session duplication).

Metadata

Metadata

Assignees

Labels

P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.no-staleExclude from stale automation

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions