Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
Matrix plugin classifies all 2-member rooms as DMs regardless of room type, making it impossible to have separate sessions for any group room that happens to have exactly 2 members (e.g. rooms in a Space, small team rooms, or topic-specific rooms). The isStrictDirectMembership function in the plugin uses a hard-coded joinedMembers.length === 2 check with no config override, so even rooms explicitly created as group rooms (with is_direct: false and no m.direct mapping) are treated as direct messages.
This causes all 2-person rooms to share a single DM session key (agent:main:matrix:direct:@user:server), even when session.dmScope is set to per-channel-peer.
Steps to reproduce
- Create a Matrix Space with multiple rooms (e.g. "Work", "Health", "Finance")
- Each room has exactly 2 members: a human user and the OpenClaw bot
- Rooms are created as regular rooms (not DMs) —
is_direct: false on member events, no m.direct account data
- Configure
session.dmScope: "per-channel-peer" in OpenClaw config
- Optionally add rooms to
channels.matrix.groups config
- Send messages in different rooms
- Run
/status in each room — all show the same session key: agent:main:matrix:direct:@user:server
Expected behavior
Each room in the Space should have its own session with a unique session key like agent:main:matrix:group:!roomId:server, similar to how Telegram topics each get their own session (agent:main:telegram:group:-xxx:topic:N).
Adding rooms to channels.matrix.groups config or having is_direct: false on room membership should be sufficient to classify them as group rooms.
Actual behavior
All rooms share one session because isStrictDirectMembership() in send-jLbjFm5r.js returns true whenever joinedMembers.length === 2:
function isStrictDirectMembership(params) {
const joinedMembers = params.joinedMembers ?? [];
return Boolean(selfUserId && remoteUserId &&
joinedMembers.length === 2 &&
joinedMembers.includes(selfUserId) &&
joinedMembers.includes(remoteUserId));
}
The isDirectMessage function in monitor-B7lcmiuj.js calls this as a fallback even when m.direct has no mapping for the room:
// Even if m.direct check fails, this fallback catches all 2-member rooms:
if (isStrictDirectMembership({ selfUserId, remoteUserId: senderId, joinedMembers })) {
log(`matrix: dm detected via exact 2-member room`);
return true;
}
The channels.matrix.groups config has no effect on this classification — it only controls access policy, not session routing.
OpenClaw version
2026.3.24 (cff6dc9)
Operating system
macOS 26.4 (arm64, Apple Silicon)
Install method
No response
Model
anthropic/claude-opus-4-6
Provider / routing chain
Direct API → Anthropic (no proxy/gateway/router). Matrix channel: OpenClaw stock plugin → Caddy reverse proxy (localhost:8008) → Continuwuity v0.5.6 homeserver
Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
Medium — workaround exists (add a third member to each room), but it's a hack. The core issue is that the plugin has no way to distinguish between actual DMs and small group rooms that happen to have 2 members.
Additional information
The 2-member DM heuristic makes sense as a fallback for actual DMs where m.direct is missing. But it should be overridable when rooms are explicitly configured in channels.matrix.groups, which is a strong signal that the operator intends them to be treated as groups.
Suggested fix
Add a config option to override DM detection for specific rooms. For example:
{
channels: {
matrix: {
groups: {
"!roomId:server": {
forceGroup: true, // Skip DM detection, always treat as group
requireMention: false
}
}
}
}
}
When forceGroup: true is set for a room in the groups config, the isDirectMessage check should return false for that room, regardless of member count.
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
Matrix plugin classifies all 2-member rooms as DMs regardless of room type, making it impossible to have separate sessions for any group room that happens to have exactly 2 members (e.g. rooms in a Space, small team rooms, or topic-specific rooms). The
isStrictDirectMembershipfunction in the plugin uses a hard-codedjoinedMembers.length === 2check with no config override, so even rooms explicitly created as group rooms (withis_direct: falseand nom.directmapping) are treated as direct messages.This causes all 2-person rooms to share a single DM session key (
agent:main:matrix:direct:@user:server), even whensession.dmScopeis set toper-channel-peer.Steps to reproduce
is_direct: falseon member events, nom.directaccount datasession.dmScope: "per-channel-peer"in OpenClaw configchannels.matrix.groupsconfig/statusin each room — all show the same session key:agent:main:matrix:direct:@user:serverExpected behavior
Each room in the Space should have its own session with a unique session key like
agent:main:matrix:group:!roomId:server, similar to how Telegram topics each get their own session (agent:main:telegram:group:-xxx:topic:N).Adding rooms to
channels.matrix.groupsconfig or havingis_direct: falseon room membership should be sufficient to classify them as group rooms.Actual behavior
All rooms share one session because
isStrictDirectMembership()insend-jLbjFm5r.jsreturnstruewheneverjoinedMembers.length === 2:The
isDirectMessagefunction inmonitor-B7lcmiuj.jscalls this as a fallback even whenm.directhas no mapping for the room:The
channels.matrix.groupsconfig has no effect on this classification — it only controls access policy, not session routing.OpenClaw version
2026.3.24 (cff6dc9)
Operating system
macOS 26.4 (arm64, Apple Silicon)
Install method
No response
Model
anthropic/claude-opus-4-6
Provider / routing chain
Direct API → Anthropic (no proxy/gateway/router). Matrix channel: OpenClaw stock plugin → Caddy reverse proxy (localhost:8008) → Continuwuity v0.5.6 homeserver
Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
Medium — workaround exists (add a third member to each room), but it's a hack. The core issue is that the plugin has no way to distinguish between actual DMs and small group rooms that happen to have 2 members.
Additional information
The 2-member DM heuristic makes sense as a fallback for actual DMs where
m.directis missing. But it should be overridable when rooms are explicitly configured inchannels.matrix.groups, which is a strong signal that the operator intends them to be treated as groups.Suggested fix
Add a config option to override DM detection for specific rooms. For example:
When
forceGroup: trueis set for a room in thegroupsconfig, theisDirectMessagecheck should returnfalsefor that room, regardless of member count.