fix(matrix): pass agentId to buildMentionRegexes for agent-level mention patterns
The Matrix monitor called buildMentionRegexes(cfg) without agentId, causing agent-level groupChat.mentionPatterns to be silently ignored. Messages matching agent-specific patterns were dropped as no-mention in rooms with requireMention: true. Defer the mention-required drop until after route resolution so the agentId is available for buildMentionRegexes(cfg, route.agentId). Media-only and poll events still drop early (no text to match). Fixes #51082
This commit is contained in:
parent
50ce9ac1c6
commit
bffe4276b4
@ -27,6 +27,7 @@ type MatrixHandlerTestHarnessOptions = {
|
|||||||
accountAllowBots?: boolean | "mentions";
|
accountAllowBots?: boolean | "mentions";
|
||||||
configuredBotUserIds?: Set<string>;
|
configuredBotUserIds?: Set<string>;
|
||||||
mentionRegexes?: MatrixMonitorHandlerParams["mentionRegexes"];
|
mentionRegexes?: MatrixMonitorHandlerParams["mentionRegexes"];
|
||||||
|
buildMentionRegexes?: MatrixMonitorHandlerParams["core"]["channel"]["mentions"]["buildMentionRegexes"];
|
||||||
groupPolicy?: "open" | "allowlist" | "disabled";
|
groupPolicy?: "open" | "allowlist" | "disabled";
|
||||||
replyToMode?: ReplyToMode;
|
replyToMode?: ReplyToMode;
|
||||||
threadReplies?: "off" | "inbound" | "always";
|
threadReplies?: "off" | "inbound" | "always";
|
||||||
@ -142,6 +143,9 @@ export function createMatrixHandlerTestHarness(
|
|||||||
resolveHumanDelayConfig: options.resolveHumanDelayConfig ?? (() => undefined),
|
resolveHumanDelayConfig: options.resolveHumanDelayConfig ?? (() => undefined),
|
||||||
dispatchReplyFromConfig,
|
dispatchReplyFromConfig,
|
||||||
},
|
},
|
||||||
|
mentions: {
|
||||||
|
buildMentionRegexes: options.buildMentionRegexes ?? (() => []),
|
||||||
|
},
|
||||||
reactions: {
|
reactions: {
|
||||||
shouldAckReaction: options.shouldAckReaction ?? (() => false),
|
shouldAckReaction: options.shouldAckReaction ?? (() => false),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -426,8 +426,8 @@ describe("matrix monitor handler pairing account scope", () => {
|
|||||||
expect(recordInboundSession).not.toHaveBeenCalled();
|
expect(recordInboundSession).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("drops forged metadata-only mentions before agent routing", async () => {
|
it("drops forged metadata-only mentions without processing", async () => {
|
||||||
const { handler, recordInboundSession, resolveAgentRoute } = createMatrixHandlerTestHarness({
|
const { handler, recordInboundSession } = createMatrixHandlerTestHarness({
|
||||||
isDirectMessage: false,
|
isDirectMessage: false,
|
||||||
mentionRegexes: [/@bot/i],
|
mentionRegexes: [/@bot/i],
|
||||||
getMemberDisplayName: async () => "sender",
|
getMemberDisplayName: async () => "sender",
|
||||||
@ -442,7 +442,6 @@ describe("matrix monitor handler pairing account scope", () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(resolveAgentRoute).not.toHaveBeenCalled();
|
|
||||||
expect(recordInboundSession).not.toHaveBeenCalled();
|
expect(recordInboundSession).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -477,9 +476,6 @@ describe("matrix monitor handler pairing account scope", () => {
|
|||||||
} as MatrixRawEvent);
|
} as MatrixRawEvent);
|
||||||
|
|
||||||
expect(downloadContent).not.toHaveBeenCalled();
|
expect(downloadContent).not.toHaveBeenCalled();
|
||||||
expect(getMemberDisplayName).not.toHaveBeenCalled();
|
|
||||||
expect(getRoomInfo).not.toHaveBeenCalled();
|
|
||||||
expect(resolveAgentRoute).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("skips poll snapshot fetches for unmentioned group poll responses", async () => {
|
it("skips poll snapshot fetches for unmentioned group poll responses", async () => {
|
||||||
@ -988,4 +984,36 @@ describe("matrix monitor handler pairing account scope", () => {
|
|||||||
|
|
||||||
expect(resolveAgentRoute).toHaveBeenCalledTimes(1);
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -494,7 +494,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { wasMentioned, hasExplicitMention } = resolveMentions({
|
let { wasMentioned, hasExplicitMention } = resolveMentions({
|
||||||
content,
|
content,
|
||||||
userId: selfUserId,
|
userId: selfUserId,
|
||||||
text: mentionPrecheckText,
|
text: mentionPrecheckText,
|
||||||
@ -554,10 +554,21 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|||||||
commandAuthorized &&
|
commandAuthorized &&
|
||||||
hasControlCommandInMessage;
|
hasControlCommandInMessage;
|
||||||
const canDetectMention = mentionRegexes.length > 0 || hasExplicitMention;
|
const canDetectMention = mentionRegexes.length > 0 || hasExplicitMention;
|
||||||
if (isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention) {
|
// 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" });
|
logger.info("skipping room message", { roomId, reason: "no-mention" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const mentionDropDeferred =
|
||||||
|
isRoom && shouldRequireMention && !wasMentioned && !shouldBypassMention;
|
||||||
|
|
||||||
if (isPollEvent) {
|
if (isPollEvent) {
|
||||||
const pollSnapshot = await fetchMatrixPollSnapshot(client, roomId, event).catch((err) => {
|
const pollSnapshot = await fetchMatrixPollSnapshot(client, roomId, event).catch((err) => {
|
||||||
@ -661,6 +672,28 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|||||||
eventTs: eventTs ?? undefined,
|
eventTs: eventTs ?? undefined,
|
||||||
resolveAgentRoute: core.channel.routing.resolveAgentRoute,
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!wasMentioned) {
|
||||||
|
logger.info("skipping room message", { roomId, reason: "no-mention" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (configuredBinding) {
|
if (configuredBinding) {
|
||||||
const ensured = await ensureConfiguredAcpBindingReady({
|
const ensured = await ensureConfiguredAcpBindingReady({
|
||||||
cfg,
|
cfg,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user