fix: thread replyToId and threadId through message tool send action (#14948)

* fix: thread replyToId and threadId through message tool send action

* fix: omit replyToId/threadId from gateway send params

* fix: add threading seam regression coverage (#14948) (thanks @mcaxtr)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Marcus Castro
2026-02-13 00:55:20 -03:00
committed by GitHub
parent 8c920b9a18
commit 13bfd9da83
6 changed files with 127 additions and 0 deletions

View File

@@ -80,6 +80,62 @@ describe("sendMessage channel normalization", () => {
});
});
describe("sendMessage replyToId threading", () => {
beforeEach(async () => {
callGatewayMock.mockReset();
vi.resetModules();
await setRegistry(emptyRegistry);
});
afterEach(async () => {
await setRegistry(emptyRegistry);
});
it("passes replyToId through to the outbound adapter", async () => {
const { sendMessage } = await loadMessage();
const capturedCtx: Record<string, unknown>[] = [];
const plugin = createMattermostLikePlugin({
onSendText: (ctx) => {
capturedCtx.push(ctx);
},
});
await setRegistry(createTestRegistry([{ pluginId: "mattermost", source: "test", plugin }]));
await sendMessage({
cfg: {},
to: "channel:town-square",
content: "thread reply",
channel: "mattermost",
replyToId: "post123",
});
expect(capturedCtx).toHaveLength(1);
expect(capturedCtx[0]?.replyToId).toBe("post123");
});
it("passes threadId through to the outbound adapter", async () => {
const { sendMessage } = await loadMessage();
const capturedCtx: Record<string, unknown>[] = [];
const plugin = createMattermostLikePlugin({
onSendText: (ctx) => {
capturedCtx.push(ctx);
},
});
await setRegistry(createTestRegistry([{ pluginId: "mattermost", source: "test", plugin }]));
await sendMessage({
cfg: {},
to: "channel:town-square",
content: "topic reply",
channel: "mattermost",
threadId: "topic456",
});
expect(capturedCtx).toHaveLength(1);
expect(capturedCtx[0]?.threadId).toBe("topic456");
});
});
describe("sendPoll channel normalization", () => {
beforeEach(async () => {
callGatewayMock.mockReset();
@@ -151,6 +207,32 @@ const createMSTeamsOutbound = (opts?: { includePoll?: boolean }): ChannelOutboun
: {}),
});
const createMattermostLikePlugin = (opts: {
onSendText: (ctx: Record<string, unknown>) => void;
}): ChannelPlugin => ({
id: "mattermost",
meta: {
id: "mattermost",
label: "Mattermost",
selectionLabel: "Mattermost",
docsPath: "/channels/mattermost",
blurb: "Mattermost test stub.",
},
capabilities: { chatTypes: ["direct", "channel"] },
config: {
listAccountIds: () => ["default"],
resolveAccount: () => ({}),
},
outbound: {
deliveryMode: "direct",
sendText: async (ctx) => {
opts.onSendText(ctx as unknown as Record<string, unknown>);
return { channel: "mattermost", messageId: "m1" };
},
sendMedia: async () => ({ channel: "mattermost", messageId: "m2" }),
},
});
const createMSTeamsPlugin = (params: {
aliases?: string[];
outbound: ChannelOutboundAdapter;