You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Matrix threads currently share the parent room's session. All messages in a room, whether room-level or inside threads, route to the same session key (agent:<agentId>:matrix:room:<roomId>). Threads should get their own isolated session keys, like Slack and Telegram already do.
Problem to solve
The Matrix plugin treats threads as reply formatting only (threadReplies config controls where the bot's message appears in the UI). There is no session-level awareness of threads. The docs confirm this:
Routing model: DMs share the agent's main session; rooms map to group sessions.
No thread-level session isolation exists. This means:
Context pollution: parallel conversations in different threads within the same room all land in a single session. The agent sees an interleaved mess of unrelated topics.
No focused conversations: starting a thread to discuss a specific task doesn't give the agent a clean slate. It carries the full room history, including unrelated threads.
No parallel workstreams: users who organize work by thread (e.g., one thread per task, per project, per debug session) get no isolation benefit. The agent conflates all threads.
Parity gap: Slack and Telegram both have thread/topic session isolation. Matrix users get a worse experience despite Matrix having solid native thread support (spec v1.4+, m.thread relation).
Proposed solution
Derive thread-specific session keys when messages arrive inside a Matrix thread:
Where $rootEventId is the thread root event ID from the m.thread relation on the inbound event.
Config
Add channels.matrix.thread.* settings following Slack's established pattern:
{channels: {matrix: {thread: {historyScope: "thread",// "thread" (per-thread, default) | "room" (shared, current behavior)inheritParent: false,// copy parent room transcript to new thread sessionsinitialHistoryLimit: 20,// fetch N existing thread messages when starting a new thread session},},},}
Behavior
historyScope
Effect
"thread" (default)
Messages inside a Matrix thread route to an isolated session keyed by thread root event ID. Thread gets its own conversation history.
"room"
Current behavior preserved , threads share the room session. Reply formatting still works via threadReplies.
inheritParent: true: when a thread session is first created, copy the parent room's recent transcript so the agent has context.
initialHistoryLimit: fetch existing thread messages when the agent joins a thread conversation mid-way.
Room-level messages (not in any thread) continue using the room session key unchanged.
Outbound messages from a thread session always reply in-thread (using m.thread + m.in_reply_to relations), regardless of the threadReplies setting, since the thread IS the session.
Implementation pointers
The Matrix plugin already distinguishes threaded vs room-level messages for inbound dispatch (#27401: sender identity preservation in threads). The m.thread relation on inbound events provides the thread root event ID.
Inbound routing: check if the incoming event has an m.thread relation. If yes and historyScope: "thread", derive a thread-specific session key.
Outbound routing: when replying from a thread session, always use m.thread relation + m.in_reply_to fallback.
Session key format: append :thread:<rootEventId> to the room session key.
E2EE: no crypto changes needed; this is purely a routing change.
Alternatives considered
1. Use separate rooms instead of threads
Each "conversation" gets its own Matrix room. Works for isolation but is heavy, room creation is slow, clutters the room list, and doesn't match how users naturally organize work in a single room with threads.
2. Prompt-prefix workaround ("focus on this thread only")
Instruct the agent via system prompt to ignore messages from other threads. Unreliable, the agent still sees the full interleaved history, burns tokens on irrelevant context, and can't reliably filter.
3. Manual /new per thread
User resets the session when switching threads. Loses all context from the current thread and is error-prone. Doesn't work for parallel threads at all since there's only one room session.
4. Use Discord/Slack instead of Matrix for thread workflows
Defeats the purpose of running Matrix for self-hosted/privacy-first setups. Matrix's thread support is mature enough — the gap is in the OpenClaw plugin, not the protocol.
Impact
Affected users/systems/channels:
All Matrix plugin users who use threads. Matrix is a plugin channel but targets a specific audience (self-hosted, privacy-focused, E2EE) that often runs OpenClaw on their own infrastructure and relies heavily on room organization via threads.
Severity:
Blocks workflow. Users who organize work by thread (common in Element) get no session isolation benefit. Every thread conversation pollutes the shared room session, making focused multi-topic work in a single room impractical.
Frequency:
Always. Every threaded message in Matrix hits this , there is no workaround that provides real thread isolation.
Consequence:
Context pollution across threads leads to confused agent responses
Token waste from irrelevant cross-thread history in every turn
No ability to run parallel workstreams per thread
This is also a prerequisite for ACP thread-bound agents on Matrix (feat: ACP thread-bound agents #23580), which requires thread session binding — impossible without thread session keys
Matrix threads (m.thread relation) are stable since spec v1.4 (MSC3440). Element and other modern clients fully support threaded views. The protocol provides everything needed — the thread root event ID is available on every threaded event and is stable/unique per thread.
Existing Matrix plugin thread awareness
The Matrix plugin already distinguishes threaded vs room-level messages:
The inbound dispatch layer knows about threads — it just doesn't use that information for session routing.
Additional information
This is a prerequisite for extending ACP thread-bound agents (feat: ACP thread-bound agents #23580) to Matrix. That feature requires thread session binding, which is impossible without thread session keys. Addressing this first makes the ACP follow-up straightforward.
Summary
Matrix threads currently share the parent room's session. All messages in a room, whether room-level or inside threads, route to the same session key (
agent:<agentId>:matrix:room:<roomId>). Threads should get their own isolated session keys, like Slack and Telegram already do.Problem to solve
The Matrix plugin treats threads as reply formatting only (
threadRepliesconfig controls where the bot's message appears in the UI). There is no session-level awareness of threads. The docs confirm this:No thread-level session isolation exists. This means:
m.threadrelation).Proposed solution
Derive thread-specific session keys when messages arrive inside a Matrix thread:
Where
$rootEventIdis the thread root event ID from them.threadrelation on the inbound event.Config
Add
channels.matrix.thread.*settings following Slack's established pattern:Behavior
historyScope"thread"(default)"room"threadReplies.inheritParent: true: when a thread session is first created, copy the parent room's recent transcript so the agent has context.initialHistoryLimit: fetch existing thread messages when the agent joins a thread conversation mid-way.m.thread+m.in_reply_torelations), regardless of thethreadRepliessetting, since the thread IS the session.Implementation pointers
The Matrix plugin already distinguishes threaded vs room-level messages for inbound dispatch (#27401: sender identity preservation in threads). The
m.threadrelation on inbound events provides the thread root event ID.m.threadrelation. If yes andhistoryScope: "thread", derive a thread-specific session key.m.threadrelation +m.in_reply_tofallback.:thread:<rootEventId>to the room session key.Alternatives considered
1. Use separate rooms instead of threads
Each "conversation" gets its own Matrix room. Works for isolation but is heavy, room creation is slow, clutters the room list, and doesn't match how users naturally organize work in a single room with threads.
2. Prompt-prefix workaround ("focus on this thread only")
Instruct the agent via system prompt to ignore messages from other threads. Unreliable, the agent still sees the full interleaved history, burns tokens on irrelevant context, and can't reliably filter.
3. Manual
/newper threadUser resets the session when switching threads. Loses all context from the current thread and is error-prone. Doesn't work for parallel threads at all since there's only one room session.
4. Use Discord/Slack instead of Matrix for thread workflows
Defeats the purpose of running Matrix for self-hosted/privacy-first setups. Matrix's thread support is mature enough — the gap is in the OpenClaw plugin, not the protocol.
Impact
Affected users/systems/channels:
All Matrix plugin users who use threads. Matrix is a plugin channel but targets a specific audience (self-hosted, privacy-focused, E2EE) that often runs OpenClaw on their own infrastructure and relies heavily on room organization via threads.
Severity:
Blocks workflow. Users who organize work by thread (common in Element) get no session isolation benefit. Every thread conversation pollutes the shared room session, making focused multi-topic work in a single room impractical.
Frequency:
Always. Every threaded message in Matrix hits this , there is no workaround that provides real thread isolation.
Consequence:
Evidence/examples
Slack (thread session isolation, shipped):
agent:<agentId>:slack:channel:<channelId>:thread:<threadTs>channels.slack.thread.historyScope(default:thread),thread.inheritParent,thread.initialHistoryLimitTelegram (topic session isolation, shipped):
agent:<agentId>:telegram:group:<chatId>:topic:<threadId>groupPolicy,requireMention, skills,systemPromptmessage_thread_idalso handled with thread-aware session keysDiscord (thread isolation + ACP thread binding, shipped):
subagent_spawninghooks (feat: ACP thread-bound agents #23580)channels.discord.threadBindings.*configMatrix protocol support
Matrix threads (
m.threadrelation) are stable since spec v1.4 (MSC3440). Element and other modern clients fully support threaded views. The protocol provides everything needed — the thread root event ID is available on every threaded event and is stable/unique per thread.Existing Matrix plugin thread awareness
The Matrix plugin already distinguishes threaded vs room-level messages:
channels.matrix.threadRepliescontrols reply formatting (off,inbound,always)channels.matrix.replyToModecontrols reply-to metadataThe inbound dispatch layer knows about threads — it just doesn't use that information for session routing.
Additional information
mode="session"requiresthread=true=> blocks non-Discord channels@openclaw/matrix), Matrix spec v1.4+