Merge a46e3282ab32d3d6c7d9e5eaacd6386165239e50 into 6b4c24c2e55b5b4013277bd799525086f6a0c40f

This commit is contained in:
Matt Van Horn 2026-03-21 04:45:11 +00:00 committed by GitHub
commit 9ffc84c373
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 75 additions and 6 deletions

View File

@ -27,6 +27,7 @@ type MatrixHandlerTestHarnessOptions = {
accountAllowBots?: boolean | "mentions";
configuredBotUserIds?: Set<string>;
mentionRegexes?: MatrixMonitorHandlerParams["mentionRegexes"];
buildMentionRegexes?: MatrixMonitorHandlerParams["core"]["channel"]["mentions"]["buildMentionRegexes"];
groupPolicy?: "open" | "allowlist" | "disabled";
replyToMode?: ReplyToMode;
threadReplies?: "off" | "inbound" | "always";
@ -177,6 +178,9 @@ export function createMatrixHandlerTestHarness(
}
}),
},
mentions: {
buildMentionRegexes: options.buildMentionRegexes ?? (() => []),
},
reactions: {
shouldAckReaction: options.shouldAckReaction ?? (() => false),
},

View File

@ -426,8 +426,8 @@ describe("matrix monitor handler pairing account scope", () => {
expect(recordInboundSession).not.toHaveBeenCalled();
});
it("drops forged metadata-only mentions before agent routing", async () => {
const { handler, recordInboundSession, resolveAgentRoute } = createMatrixHandlerTestHarness({
it("drops forged metadata-only mentions without processing", async () => {
const { handler, recordInboundSession } = createMatrixHandlerTestHarness({
isDirectMessage: false,
mentionRegexes: [/@bot/i],
getMemberDisplayName: async () => "sender",
@ -442,7 +442,6 @@ describe("matrix monitor handler pairing account scope", () => {
}),
);
expect(resolveAgentRoute).not.toHaveBeenCalled();
expect(recordInboundSession).not.toHaveBeenCalled();
});
@ -1012,6 +1011,38 @@ describe("matrix monitor handler pairing account scope", () => {
expect(resolveAgentRoute).toHaveBeenCalledTimes(1);
});
it("re-resolves mentions with agent-level patterns after route resolution (#51082)", async () => {
const buildMentionRegexes = vi.fn((_cfg: unknown, agentId?: string) => {
if (!agentId) return [];
return [/@mybot/i];
});
const dispatchReplyFromConfig = vi.fn(async () => ({
queuedFinal: false,
counts: { final: 0, block: 0, tool: 0 },
}));
const { handler } = createMatrixHandlerTestHarness({
mentionRegexes: [],
buildMentionRegexes,
roomsConfig: {
"!room:example.org": { requireMention: true },
},
isDirectMessage: false,
groupPolicy: "open",
dispatchReplyFromConfig,
});
await handler(
"!room:example.org",
createMatrixTextMessageEvent({
eventId: "$agent-mention",
body: "hey @mybot can you help?",
}),
);
expect(buildMentionRegexes).toHaveBeenCalledWith(expect.anything(), "ops");
expect(dispatchReplyFromConfig).toHaveBeenCalled();
});
});
describe("matrix monitor handler durable inbound dedupe", () => {

View File

@ -527,7 +527,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
return;
}
const { wasMentioned, hasExplicitMention } = resolveMentions({
let { wasMentioned, hasExplicitMention } = resolveMentions({
content,
userId: selfUserId,
text: mentionPrecheckText,
@ -588,12 +588,23 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
!hasExplicitMention &&
commandAuthorized &&
hasControlCommandInMessage;
const canDetectMention = mentionRegexes.length > 0 || hasExplicitMention;
if (isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention) {
let canDetectMention = mentionRegexes.length > 0 || hasExplicitMention;
// When there is message text, defer mention drop until after route
// resolution so agent-level mentionPatterns are checked (#51082).
// Media-only/poll events have no text - drop them immediately.
if (
isRoom &&
shouldRequireMention &&
!wasMentioned &&
!shouldBypassMention &&
!mentionPrecheckText
) {
logger.info("skipping room message", { roomId, reason: "no-mention" });
await commitInboundEventIfClaimed();
return;
}
const mentionDropDeferred =
isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention;
if (isPollEvent) {
const pollSnapshot = await fetchMatrixPollSnapshot(client, roomId, event).catch((err) => {
@ -698,6 +709,29 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
eventTs: eventTs ?? undefined,
resolveAgentRoute: core.channel.routing.resolveAgentRoute,
});
// Re-resolve mentions with agent-specific mentionPatterns now that the
// route (and agentId) is known (#51082).
if (mentionDropDeferred) {
const agentMentionRegexes = core.channel.mentions.buildMentionRegexes(cfg, route.agentId);
if (agentMentionRegexes.length > 0) {
const agentMentionResult = resolveMentions({
content,
userId: selfUserId,
text: mentionPrecheckText,
mentionRegexes: agentMentionRegexes,
});
if (agentMentionResult.wasMentioned) {
wasMentioned = true;
canDetectMention = true;
}
}
if (!wasMentioned) {
logger.info("skipping room message", { roomId, reason: "no-mention" });
return;
}
}
if (configuredBinding) {
const ensured = await ensureConfiguredAcpBindingReady({
cfg,