mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
fix(feishu): suppress reasoning/thinking block payloads from delivery (#31723)
* fix(extensions/feishu/src/reply-dispatcher.ts): missing privacy check / data leak Pattern from PR #24969 The fix addresses the critical race condition by placing the 'block' filter check at the very top of the `deliver` function. This ensures that for internal 'block' reasoning chunks, the function returns immediately, preventing any text processing (lines 195-203) and, crucially, preventing the initialization of the streaming state for these payloads (lines 212-216). This ensures that the `streaming` object is not initialized with empty data, and subsequent 'final' payloads will correctly initialize and stream only the final content. The fix also addresses the 'incomplete' validation issue by using `info?.kind !== 'block'`. While the contract likely ensures `info` is present, this defensive approach ensures that if `info` is missing (and the payload is unrelated to internal blocking), the message is still delivered to the user, preventing a 'silent failure' bug. The validation logic at line 205 (`!hasText && !hasMedia`) ensures we do not send empty messages. * Fix indentation: remove extra 4 spaces from deliver function body The deliver function is inside the createReplyDispatcherWithTyping call, so it should be indented at 2 levels (8 spaces), not 3 levels (12 spaces). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(feishu): cover block payload suppression in reply dispatcher --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -185,6 +185,23 @@ describe("createFeishuReplyDispatcher streaming behavior", () => {
|
||||
expect(sendMarkdownCardFeishuMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("suppresses internal block payload delivery", async () => {
|
||||
createFeishuReplyDispatcher({
|
||||
cfg: {} as never,
|
||||
agentId: "agent",
|
||||
runtime: {} as never,
|
||||
chatId: "oc_chat",
|
||||
});
|
||||
|
||||
const options = createReplyDispatcherWithTypingMock.mock.calls[0]?.[0];
|
||||
await options.deliver({ text: "internal reasoning chunk" }, { kind: "block" });
|
||||
|
||||
expect(streamingInstances).toHaveLength(0);
|
||||
expect(sendMessageFeishuMock).not.toHaveBeenCalled();
|
||||
expect(sendMarkdownCardFeishuMock).not.toHaveBeenCalled();
|
||||
expect(sendMediaFeishuMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses streaming session for auto mode markdown payloads", async () => {
|
||||
createFeishuReplyDispatcher({
|
||||
cfg: {} as never,
|
||||
|
||||
@@ -192,6 +192,12 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
||||
void typingCallbacks.onReplyStart?.();
|
||||
},
|
||||
deliver: async (payload: ReplyPayload, info) => {
|
||||
// FIX: Filter out internal 'block' reasoning chunks immediately to prevent
|
||||
// data leak and race conditions with streaming state initialization.
|
||||
if (info?.kind === "block") {
|
||||
return;
|
||||
}
|
||||
|
||||
const text = payload.text ?? "";
|
||||
const mediaList =
|
||||
payload.mediaUrls && payload.mediaUrls.length > 0
|
||||
@@ -209,7 +215,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
||||
if (hasText) {
|
||||
const useCard = renderMode === "card" || (renderMode === "auto" && shouldUseCard(text));
|
||||
|
||||
if ((info?.kind === "block" || info?.kind === "final") && streamingEnabled && useCard) {
|
||||
if (info?.kind === "final" && streamingEnabled && useCard) {
|
||||
startStreaming();
|
||||
if (streamingStartPromise) {
|
||||
await streamingStartPromise;
|
||||
|
||||
Reference in New Issue
Block a user