From 52865579e0ccd0303764d3c69fc868afe5820640 Mon Sep 17 00:00:00 2001 From: bmendonca3 <208517100+bmendonca3@users.noreply.github.com> Date: Sat, 28 Feb 2026 10:52:13 -0700 Subject: [PATCH] Feishu: reply to topic roots --- extensions/feishu/src/bot.test.ts | 38 +++++++++++++++++++++++++++++++ extensions/feishu/src/bot.ts | 4 ++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/extensions/feishu/src/bot.test.ts b/extensions/feishu/src/bot.test.ts index 3247dea9b65..22e300025e1 100644 --- a/extensions/feishu/src/bot.test.ts +++ b/extensions/feishu/src/bot.test.ts @@ -1397,6 +1397,44 @@ describe("handleFeishuMessage command authorization", () => { ); }); + it("replies to the topic root when handling a message inside an existing topic", async () => { + mockShouldComputeCommandAuthorized.mockReturnValue(false); + + const cfg: ClawdbotConfig = { + channels: { + feishu: { + groups: { + "oc-group": { + requireMention: false, + replyInThread: "enabled", + }, + }, + }, + }, + } as ClawdbotConfig; + + const event: FeishuMessageEvent = { + sender: { sender_id: { open_id: "ou-topic-user" } }, + message: { + message_id: "om_child_message", + root_id: "om_root_topic", + chat_id: "oc-group", + chat_type: "group", + message_type: "text", + content: JSON.stringify({ text: "reply inside topic" }), + }, + }; + + await dispatchMessage({ cfg, event }); + + expect(mockCreateFeishuReplyDispatcher).toHaveBeenCalledWith( + expect.objectContaining({ + replyToMessageId: "om_root_topic", + rootId: "om_root_topic", + }), + ); + }); + it("forces thread replies when inbound message contains thread_id", async () => { mockShouldComputeCommandAuthorized.mockReturnValue(false); diff --git a/extensions/feishu/src/bot.ts b/extensions/feishu/src/bot.ts index 9bfac4d4d18..16fbac55da9 100644 --- a/extensions/feishu/src/bot.ts +++ b/extensions/feishu/src/bot.ts @@ -1231,13 +1231,13 @@ export async function handleFeishuMessage(params: { const messageCreateTimeMs = event.message.create_time ? parseInt(event.message.create_time, 10) : undefined; - + const replyTargetMessageId = ctx.rootId ?? ctx.messageId; const { dispatcher, replyOptions, markDispatchIdle } = createFeishuReplyDispatcher({ cfg, agentId: route.agentId, runtime: runtime as RuntimeEnv, chatId: ctx.chatId, - replyToMessageId: ctx.messageId, + replyToMessageId: replyTargetMessageId, skipReplyToInMessages: !isGroup, replyInThread, rootId: ctx.rootId,