Matrix: align reaction routing with thread bindings

This commit is contained in:
Gustavo Madeira Santana
2026-03-09 04:42:41 -04:00
parent e86857cb63
commit 757bfebc31
2 changed files with 83 additions and 7 deletions

View File

@@ -314,6 +314,68 @@ describe("matrix monitor handler pairing account scope", () => {
);
});
it("routes reaction notifications for bound thread messages to the bound session", async () => {
registerSessionBindingAdapter({
channel: "matrix",
accountId: "ops",
listBySession: () => [],
resolveByConversation: (ref) =>
ref.conversationId === "$root"
? {
bindingId: "ops:!room:example.org:$root",
targetSessionKey: "agent:bound:session-1",
targetKind: "session",
conversation: {
channel: "matrix",
accountId: "ops",
conversationId: "$root",
parentConversationId: "!room:example.org",
},
status: "active",
boundAt: Date.now(),
metadata: {
boundBy: "user-1",
},
}
: null,
touch: vi.fn(),
});
const { handler, enqueueSystemEvent } = createMatrixHandlerTestHarness({
client: {
getEvent: async () =>
createMatrixTextMessageEvent({
eventId: "$reply1",
sender: "@bot:example.org",
body: "follow up",
relatesTo: {
rel_type: "m.thread",
event_id: "$root",
"m.in_reply_to": { event_id: "$root" },
},
}),
},
isDirectMessage: false,
});
await handler(
"!room:example.org",
createMatrixReactionEvent({
eventId: "$reaction-thread",
targetEventId: "$reply1",
key: "🎯",
}),
);
expect(enqueueSystemEvent).toHaveBeenCalledWith(
"Matrix reaction added: 🎯 by sender on msg $reply1",
{
sessionKey: "agent:bound:session-1",
contextKey: "matrix:reaction:add:!room:example.org:$reply1:@user:example.org:🎯",
},
);
});
it("ignores reactions that do not target bot-authored messages", async () => {
const { handler, enqueueSystemEvent, resolveAgentRoute } = createReactionHarness({
targetSender: "@other:example.org",

View File

@@ -3,7 +3,9 @@ import type { CoreConfig } from "../../types.js";
import { resolveMatrixAccountConfig } from "../accounts.js";
import { extractMatrixReactionAnnotation } from "../reaction-common.js";
import type { MatrixClient } from "../sdk.js";
import type { MatrixRawEvent } from "./types.js";
import { resolveMatrixInboundRoute } from "./route.js";
import { resolveMatrixThreadRootId } from "./threads.js";
import type { MatrixRawEvent, RoomMessageEventContent } from "./types.js";
export type MatrixReactionNotificationMode = "off" | "own";
@@ -60,14 +62,26 @@ export async function handleInboundMatrixReaction(params: {
return;
}
const route = params.core.channel.routing.resolveAgentRoute({
const targetContent =
targetEvent && targetEvent.content && typeof targetEvent.content === "object"
? (targetEvent.content as RoomMessageEventContent)
: undefined;
const threadRootId = targetContent
? resolveMatrixThreadRootId({
event: targetEvent as MatrixRawEvent,
content: targetContent,
})
: undefined;
const { route } = resolveMatrixInboundRoute({
cfg: params.cfg,
channel: "matrix",
accountId: params.accountId,
peer: {
kind: params.isDirectMessage ? "direct" : "channel",
id: params.isDirectMessage ? params.senderId : params.roomId,
},
roomId: params.roomId,
senderId: params.senderId,
isDirectMessage: params.isDirectMessage,
messageId: reaction.eventId,
threadRootId,
eventTs: params.event.origin_server_ts,
resolveAgentRoute: params.core.channel.routing.resolveAgentRoute,
});
const text = `Matrix reaction added: ${reaction.key} by ${params.senderLabel} on msg ${reaction.eventId}`;
params.core.system.enqueueSystemEvent(text, {