Skip to content

Commit f20073a

Browse files
committed
fix(feishu): omit both replyToMessageId and rootId from streaming.start() in DM chats
In DM chats (skipReplyToInMessages=true), the streaming card start() previously received the raw replyToMessageId and rootId, causing resolveStreamingCardSendMode to choose "reply" or "root_create" mode instead of "create". This mismatched the behavior of plain-text and static-card sends, which omit reply metadata in DMs. - Pass sendReplyToMessageId (already undefined in DM) to streaming.start() - Add streamingRootId: undefined in DM (skipReplyToInMessages=true), preserving rootId for group/thread streaming - Add regression tests for DM (both fields omitted) and group (both preserved) streaming card send modes Closes #94922
1 parent df8a19e commit f20073a

2 files changed

Lines changed: 31 additions & 6 deletions

File tree

extensions/feishu/src/reply-dispatcher.test.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,25 +1565,46 @@ describe("createFeishuReplyDispatcher streaming behavior", () => {
15651565
});
15661566
});
15671567

1568-
it("omits replyToMessageId from streaming.start() in DM chats (skipReplyToInMessages)", async () => {
1569-
// In DM chats, skipReplyToInMessages=true means sendReplyToMessageId=undefined.
1570-
// The streaming card start() should receive undefined replyToMessageId,
1571-
// so resolveStreamingCardSendMode returns "create" (no Reply-to label in DM).
1568+
it("omits both replyToMessageId and rootId from streaming.start() in DM chats (skipReplyToInMessages)", async () => {
1569+
// In DM chats, skipReplyToInMessages=true means both sendReplyToMessageId and
1570+
// streamingRootId are undefined. The streaming card start() should receive
1571+
// undefined for both, so resolveStreamingCardSendMode returns "create" (no
1572+
// Reply-to label, no root_create fallback in DM).
15721573
const { options } = createDispatcherHarness({
15731574
runtime: createRuntimeLogger(),
1574-
replyToMessageId: "om_dm_msg",
1575+
replyToMessageId: "dm_msg",
15751576
skipReplyToInMessages: true,
1577+
rootId: "dm_root",
15761578
});
15771579
await options.deliver({ text: "```ts\nconst x = 1\n```" }, { kind: "final" });
15781580

15791581
expect(streamingInstances).toHaveLength(1);
15801582
expectStreamingStartOptions(0, {
15811583
replyToMessageId: undefined,
1584+
rootId: undefined,
15821585
header: { title: "agent", template: "blue" },
15831586
note: "Agent: agent",
15841587
});
15851588
});
15861589

1590+
it("preserves rootId for group/thread streaming when skipReplyToInMessages is false", async () => {
1591+
// In group chats, skipReplyToInMessages=false so rootId is passed through,
1592+
// enabling root_create mode for proper threading.
1593+
const { options } = createDispatcherHarness({
1594+
runtime: createRuntimeLogger(),
1595+
replyToMessageId: "group_msg",
1596+
skipReplyToInMessages: false,
1597+
rootId: "group_root",
1598+
});
1599+
await options.deliver({ text: "```ts\nconst x = 1\n```" }, { kind: "final" });
1600+
1601+
expect(streamingInstances).toHaveLength(1);
1602+
expectStreamingStartOptions(0, {
1603+
replyToMessageId: "group_msg",
1604+
rootId: "group_root",
1605+
});
1606+
});
1607+
15871608
it("uses streaming cards for thread replies and keeps topic metadata", async () => {
15881609
const { options } = createDispatcherHarness({
15891610
runtime: createRuntimeLogger(),

extensions/feishu/src/reply-dispatcher.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
171171
mentionTargets,
172172
} = params;
173173
const sendReplyToMessageId = skipReplyToInMessages ? undefined : replyToMessageId;
174+
// In DM chats (skipReplyToInMessages=true), ordinary DM rootId should also be
175+
// omitted so streaming.start() chooses "create" mode, matching plain-text and
176+
// static-card sends. Group/thread rootId is preserved for correct threading.
177+
const streamingRootId = skipReplyToInMessages ? undefined : rootId;
174178
const typingTargetMessageId = explicitTypingTargetMessageId?.trim() || replyToMessageId;
175179
const threadReplyMode = threadReply === true;
176180
const effectiveReplyInThread = threadReplyMode ? true : replyInThread;
@@ -394,7 +398,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
394398
await streaming.start(chatId, resolveReceiveIdType(chatId), {
395399
replyToMessageId: sendReplyToMessageId,
396400
replyInThread: effectiveReplyInThread,
397-
rootId,
401+
rootId: streamingRootId,
398402
header: cardHeader,
399403
note: cardNote,
400404
});

0 commit comments

Comments
 (0)