From b5a602596f7a699bf4c2536d3080bb2c0faf7136 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Sun, 5 Apr 2026 13:11:30 -0400 Subject: [PATCH] fix(matrix): allow per-room reuse without stored account metadata --- docs/gateway/configuration-reference.md | 1 + extensions/matrix/src/session-route.test.ts | 57 +++++++++++++++++++++ extensions/matrix/src/session-route.ts | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/docs/gateway/configuration-reference.md b/docs/gateway/configuration-reference.md index 50ac90b172e..63224be8c07 100644 --- a/docs/gateway/configuration-reference.md +++ b/docs/gateway/configuration-reference.md @@ -655,6 +655,7 @@ Matrix is extension-backed and configured under `channels.matrix`. - `sessionFilter`: optional session key patterns (substring or regex). - `target`: where to send approval prompts. `"dm"` (default), `"channel"` (originating room), or `"both"`. - Per-account overrides: `channels.matrix.accounts..execApprovals`. +- `channels.matrix.dm.sessionScope` controls how Matrix DMs group into sessions: `per-user` (default) shares by routed peer, while `per-room` isolates each DM room. - Matrix status probes and live directory lookups use the same proxy policy as runtime traffic. - Full Matrix configuration, targeting rules, and setup examples are documented in [Matrix](/channels/matrix). diff --git a/extensions/matrix/src/session-route.test.ts b/extensions/matrix/src/session-route.test.ts index 9df02d486b0..a1ae85a97b0 100644 --- a/extensions/matrix/src/session-route.test.ts +++ b/extensions/matrix/src/session-route.test.ts @@ -304,4 +304,61 @@ describe("resolveMatrixOutboundSessionRoute", () => { to: "room:!dm:example.org", }); }); + + it("reuses the current DM room when stored account metadata is missing", () => { + const storePath = createTempStore({ + "agent:main:matrix:channel:!dm:example.org": { + sessionId: "sess-1", + updatedAt: Date.now(), + chatType: "direct", + origin: { + chatType: "direct", + from: "matrix:@alice:example.org", + to: "room:!dm:example.org", + }, + deliveryContext: { + channel: "matrix", + to: "room:!dm:example.org", + }, + }, + }); + const cfg = { + session: { + store: storePath, + }, + channels: { + matrix: { + defaultAccount: "ops", + accounts: { + ops: { + dm: { + sessionScope: "per-room", + }, + }, + }, + }, + }, + } satisfies OpenClawConfig; + + const route = resolveMatrixOutboundSessionRoute({ + cfg, + agentId: "main", + currentSessionKey: "agent:main:matrix:channel:!dm:example.org", + target: "@alice:example.org", + resolvedTarget: { + to: "@alice:example.org", + kind: "user", + source: "normalized", + }, + }); + + expect(route).toMatchObject({ + sessionKey: "agent:main:matrix:channel:!dm:example.org", + baseSessionKey: "agent:main:matrix:channel:!dm:example.org", + peer: { kind: "channel", id: "!dm:example.org" }, + chatType: "direct", + from: "matrix:@alice:example.org", + to: "room:!dm:example.org", + }); + }); }); diff --git a/extensions/matrix/src/session-route.ts b/extensions/matrix/src/session-route.ts index e36edfbff28..cb40bf9e309 100644 --- a/extensions/matrix/src/session-route.ts +++ b/extensions/matrix/src/session-route.ts @@ -55,7 +55,7 @@ function resolveMatrixCurrentDmRoomId(params: { if (!currentSession) { return undefined; } - if (!currentSession.accountId || currentSession.accountId !== params.accountId) { + if (currentSession.accountId && currentSession.accountId !== params.accountId) { return undefined; } if (!currentSession.directUserId || currentSession.directUserId !== params.targetUserId) {