diff --git a/extensions/matrix/src/matrix/monitor/mentions.test.ts b/extensions/matrix/src/matrix/monitor/mentions.test.ts index ca1f872d874..5553f131292 100644 --- a/extensions/matrix/src/matrix/monitor/mentions.test.ts +++ b/extensions/matrix/src/matrix/monitor/mentions.test.ts @@ -34,19 +34,22 @@ describe("resolveMentions", () => { expect(result.hasExplicitMention).toBe(true); }); - it("does not trust forged m.mentions.user_ids without a visible mention", () => { + it("detects mention via m.mentions.user_ids even without visible text mention (#64785)", () => { + // MSC3952: m.mentions.user_ids is the authoritative mention source. + // Non-OpenClaw Matrix clients (Element, standalone bots) may set + // m.mentions without including @bot in the visible message body. const result = resolveMentions({ content: { msgtype: "m.text", - body: "hello", + body: "please reply", "m.mentions": { user_ids: ["@bot:matrix.org"] }, }, userId, - text: "hello", + text: "please reply", mentionRegexes, }); - expect(result.wasMentioned).toBe(false); - expect(result.hasExplicitMention).toBe(false); + expect(result.wasMentioned).toBe(true); + expect(result.hasExplicitMention).toBe(true); }); it("detects room mention via visible @room text", () => { diff --git a/extensions/matrix/src/matrix/monitor/mentions.ts b/extensions/matrix/src/matrix/monitor/mentions.ts index e3c3556f602..dfba68d9cb0 100644 --- a/extensions/matrix/src/matrix/monitor/mentions.ts +++ b/extensions/matrix/src/matrix/monitor/mentions.ts @@ -163,10 +163,13 @@ export function resolveMentions(params: { mentionRegexes: params.mentionRegexes, }) : false; + // m.mentions.user_ids is the authoritative mention source per MSC3952. + // Previously this also required a visible text or formatted_body mention, + // which caused messages from non-OpenClaw clients that send proper + // m.mentions metadata without an @-mention in the body to be silently + // ignored when requireMention was enabled (#64785). const metadataBackedUserMention = Boolean( - params.userId && - mentionedUsers.has(params.userId) && - (mentionedInFormattedBody || textMentioned), + params.userId && mentionedUsers.has(params.userId), ); const metadataBackedRoomMention = Boolean(mentions?.room) && visibleRoomMention; const explicitMention =