From 49c60e9065d98a6848e62c717315eb91eeaa6038 Mon Sep 17 00:00:00 2001 From: peetzweg/ <839848+peetzweg@users.noreply.github.com> Date: Tue, 10 Feb 2026 01:16:40 +0100 Subject: [PATCH] feat(matrix): add thread session isolation (#8241) Co-authored-by: Claude Opus 4.5 --- .../matrix/src/matrix/monitor/handler.ts | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/extensions/matrix/src/matrix/monitor/handler.ts b/extensions/matrix/src/matrix/monitor/handler.ts index 08f255b5a..eef2bed43 100644 --- a/extensions/matrix/src/matrix/monitor/handler.ts +++ b/extensions/matrix/src/matrix/monitor/handler.ts @@ -12,6 +12,7 @@ import { } from "openclaw/plugin-sdk"; import type { CoreConfig, MatrixRoomConfig, ReplyToMode } from "../../types.js"; import type { MatrixRawEvent, RoomMessageEventContent } from "./types.js"; +import { fetchEventSummary } from "../actions/summary.js"; import { formatPollAsText, isPollStartType, @@ -431,7 +432,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam isThreadRoot: false, // @vector-im/matrix-bot-sdk doesn't have this info readily available }); - const route = core.channel.routing.resolveAgentRoute({ + const baseRoute = core.channel.routing.resolveAgentRoute({ cfg, channel: "matrix", peer: { @@ -439,8 +440,57 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam id: isDirectMessage ? senderId : roomId, }, }); + + const route = { + ...baseRoute, + sessionKey: threadRootId + ? `${baseRoute.sessionKey}:thread:${threadRootId}` + : baseRoute.sessionKey, + }; + + let threadStarterBody: string | undefined; + let threadLabel: string | undefined; + let parentSessionKey: string | undefined; + + if (threadRootId) { + const existingSession = core.channel.session.readSessionUpdatedAt({ + storePath: core.channel.session.resolveStorePath(cfg.session?.store, { + agentId: baseRoute.agentId, + }), + sessionKey: route.sessionKey, + }); + + if (existingSession === undefined) { + try { + const rootEvent = await fetchEventSummary(client, roomId, threadRootId); + if (rootEvent?.body) { + const rootSenderName = rootEvent.sender + ? await getMemberDisplayName(roomId, rootEvent.sender) + : undefined; + + threadStarterBody = core.channel.reply.formatAgentEnvelope({ + channel: "Matrix", + from: rootSenderName ?? rootEvent.sender ?? "Unknown", + timestamp: rootEvent.timestamp, + envelope: core.channel.reply.resolveEnvelopeFormatOptions(cfg), + body: rootEvent.body, + }); + + threadLabel = `Matrix thread in ${roomName ?? roomId}`; + parentSessionKey = baseRoute.sessionKey; + } + } catch (err) { + logVerboseMessage( + `matrix: failed to fetch thread root ${threadRootId}: ${String(err)}`, + ); + } + } + } + const envelopeFrom = isDirectMessage ? senderName : (roomName ?? roomId); - const textWithId = `${bodyText}\n[matrix event id: ${messageId} room: ${roomId}]`; + const textWithId = threadRootId + ? `${bodyText}\n[matrix event id: ${messageId} room: ${roomId} thread: ${threadRootId}]` + : `${bodyText}\n[matrix event id: ${messageId} room: ${roomId}]`; const storePath = core.channel.session.resolveStorePath(cfg.session?.store, { agentId: route.agentId, }); @@ -467,7 +517,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam To: `room:${roomId}`, SessionKey: route.sessionKey, AccountId: route.accountId, - ChatType: isDirectMessage ? "direct" : "channel", + ChatType: threadRootId ? "thread" : isDirectMessage ? "direct" : "channel", ConversationLabel: envelopeFrom, SenderName: senderName, SenderId: senderId, @@ -490,6 +540,9 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam CommandSource: "text" as const, OriginatingChannel: "matrix" as const, OriginatingTo: `room:${roomId}`, + ThreadStarterBody: threadStarterBody, + ThreadLabel: threadLabel, + ParentSessionKey: parentSessionKey, }); await core.channel.session.recordInboundSession({