fix(slack): preserve thread aliases in runtime outbound sends (#62947)

Slack-threaded direct sends that go through the generic runtime wrapper now stay in the intended thread when the caller supplies threadTs.
This commit is contained in:
Bek
2026-04-21 17:40:47 -04:00
committed by GitHub
parent 475e6ff1d1
commit 49b233caa1
2 changed files with 59 additions and 1 deletions

View File

@@ -115,6 +115,63 @@ describe("createChannelOutboundRuntimeSend", () => {
);
});
it("forwards Slack threadTs alias to threadId", async () => {
const sendText = vi.fn(async () => ({ channel: "slack", messageId: "slack-1" }));
mocks.loadChannelOutboundAdapter.mockResolvedValue({
sendText,
});
const { createChannelOutboundRuntimeSend } = await import("./channel-outbound-send.js");
const runtimeSend = createChannelOutboundRuntimeSend({
channelId: "slack" as never,
unavailableMessage: "unavailable",
});
await runtimeSend.sendMessage("C123", "hello", {
cfg: {},
threadTs: "1712345678.123456",
});
expect(sendText).toHaveBeenCalledWith(
expect.objectContaining({
cfg: {},
to: "C123",
text: "hello",
threadId: "1712345678.123456",
}),
);
});
it("prefers canonical thread fields over Slack aliases", async () => {
const sendText = vi.fn(async () => ({ channel: "slack", messageId: "slack-2" }));
mocks.loadChannelOutboundAdapter.mockResolvedValue({
sendText,
});
const { createChannelOutboundRuntimeSend } = await import("./channel-outbound-send.js");
const runtimeSend = createChannelOutboundRuntimeSend({
channelId: "slack" as never,
unavailableMessage: "unavailable",
});
await runtimeSend.sendMessage("C123", "hello", {
cfg: {},
messageThreadId: "200.000",
threadId: "150.000",
threadTs: "100.000",
replyToMessageId: "400.000",
replyToId: "300.000",
});
expect(sendText).toHaveBeenCalledWith(
expect.objectContaining({
cfg: {},
threadId: "200.000",
replyToId: "400.000",
}),
);
});
it("falls back to sendText when media is present but sendMedia is unavailable", async () => {
const sendText = vi.fn(async () => ({ channel: "whatsapp", messageId: "wa-3" }));
mocks.loadChannelOutboundAdapter.mockResolvedValue({

View File

@@ -14,6 +14,7 @@ type RuntimeSendOpts = {
accountId?: string;
threadId?: string | number | null;
messageThreadId?: string | number;
threadTs?: string | number;
replyToId?: string | number | null;
replyToMessageId?: string | number;
silent?: boolean;
@@ -23,7 +24,7 @@ type RuntimeSendOpts = {
};
function resolveRuntimeThreadId(opts: RuntimeSendOpts): string | number | undefined {
return opts.messageThreadId ?? opts.threadId ?? undefined;
return opts.messageThreadId ?? opts.threadId ?? opts.threadTs ?? undefined;
}
function resolveRuntimeReplyToId(opts: RuntimeSendOpts): string | undefined {