fix: pass rootId to streaming card in Feishu topic groups (openclaw#28346) thanks @Sid-Qin

Verified:
- pnpm check
- pnpm test extensions/feishu/src/reply-dispatcher.test.ts

Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
Sid
2026-02-28 10:20:53 +08:00
committed by GitHub
parent da00ead652
commit 4221b5f809
5 changed files with 36 additions and 8 deletions

View File

@@ -1019,6 +1019,7 @@ export async function handleFeishuMessage(params: {
chatId: ctx.chatId,
replyToMessageId: ctx.messageId,
replyInThread,
rootId: ctx.rootId,
mentionTargets: ctx.mentionTargets,
accountId: account.accountId,
});

View File

@@ -105,6 +105,7 @@ describe("createFeishuReplyDispatcher streaming behavior", () => {
agentId: "agent",
runtime: { log: vi.fn(), error: vi.fn() } as never,
chatId: "oc_chat",
rootId: "om_root_topic",
});
const options = createReplyDispatcherWithTypingMock.mock.calls[0]?.[0];
@@ -112,6 +113,11 @@ describe("createFeishuReplyDispatcher streaming behavior", () => {
expect(streamingInstances).toHaveLength(1);
expect(streamingInstances[0].start).toHaveBeenCalledTimes(1);
expect(streamingInstances[0].start).toHaveBeenCalledWith("oc_chat", "chat_id", {
replyToMessageId: undefined,
replyInThread: undefined,
rootId: "om_root_topic",
});
expect(streamingInstances[0].close).toHaveBeenCalledTimes(1);
expect(sendMessageFeishuMock).not.toHaveBeenCalled();
expect(sendMarkdownCardFeishuMock).not.toHaveBeenCalled();

View File

@@ -29,14 +29,23 @@ export type CreateFeishuReplyDispatcherParams = {
chatId: string;
replyToMessageId?: string;
replyInThread?: boolean;
rootId?: string;
mentionTargets?: MentionTarget[];
accountId?: string;
};
export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherParams) {
const core = getFeishuRuntime();
const { cfg, agentId, chatId, replyToMessageId, replyInThread, mentionTargets, accountId } =
params;
const {
cfg,
agentId,
chatId,
replyToMessageId,
replyInThread,
rootId,
mentionTargets,
accountId,
} = params;
const account = resolveFeishuAccount({ cfg, accountId });
const prefixContext = createReplyPrefixContext({ cfg, agentId });
@@ -105,6 +114,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
await streaming.start(chatId, resolveReceiveIdType(chatId), {
replyToMessageId,
replyInThread,
rootId,
});
} catch (error) {
params.runtime.error?.(`feishu: streaming start failed: ${String(error)}`);

View File

@@ -99,7 +99,7 @@ export class FeishuStreamingSession {
async start(
receiveId: string,
receiveIdType: "open_id" | "user_id" | "union_id" | "email" | "chat_id" = "chat_id",
options?: { replyToMessageId?: string; replyInThread?: boolean },
options?: { replyToMessageId?: string; replyInThread?: boolean; rootId?: string },
): Promise<void> {
if (this.state) {
return;
@@ -144,9 +144,20 @@ export class FeishuStreamingSession {
const cardId = createData.data.card_id;
const cardContent = JSON.stringify({ type: "card", data: { card_id: cardId } });
// Send card message — reply into thread when configured
// Topic-group replies require root_id routing. Prefer create+root_id when available.
let sendRes;
if (options?.replyToMessageId) {
if (options?.rootId) {
const createData = {
receive_id: receiveId,
msg_type: "interactive",
content: cardContent,
root_id: options.rootId,
};
sendRes = await this.client.im.message.create({
params: { receive_id_type: receiveIdType },
data: createData,
});
} else if (options?.replyToMessageId) {
sendRes = await this.client.im.message.reply({
path: { message_id: options.replyToMessageId },
data: {

View File

@@ -24,9 +24,9 @@ const sourceRoots = [
const allowedRawFetchCallsites = new Set([
"extensions/bluebubbles/src/types.ts:131",
"extensions/feishu/src/streaming-card.ts:31",
"extensions/feishu/src/streaming-card.ts:100",
"extensions/feishu/src/streaming-card.ts:141",
"extensions/feishu/src/streaming-card.ts:197",
"extensions/feishu/src/streaming-card.ts:101",
"extensions/feishu/src/streaming-card.ts:143",
"extensions/feishu/src/streaming-card.ts:199",
"extensions/google-gemini-cli-auth/oauth.ts:372",
"extensions/google-gemini-cli-auth/oauth.ts:408",
"extensions/google-gemini-cli-auth/oauth.ts:447",