fix(discord): keep progress drafts through interim blocks

This commit is contained in:
Peter Steinberger
2026-05-03 22:58:47 +01:00
parent d3ee67b420
commit dd32254607
3 changed files with 77 additions and 1 deletions

View File

@@ -103,6 +103,9 @@ export function createDiscordDraftPreviewController(params: {
draftStream,
previewToolProgressEnabled,
suppressDefaultToolProgressMessages,
get isProgressMode() {
return discordStreamMode === "progress";
},
get finalizedViaPreviewMessage() {
return finalizedViaPreviewMessage;
},
@@ -252,7 +255,12 @@ export function createDiscordDraftPreviewController(params: {
},
});
},
handleAssistantMessageBoundary: forceNewMessageIfNeeded,
handleAssistantMessageBoundary() {
if (discordStreamMode === "progress") {
return;
}
forceNewMessageIfNeeded();
},
async flush() {
if (!draftStream) {
return;

View File

@@ -1500,6 +1500,68 @@ describe("processDiscordMessage draft streaming", () => {
expect(draftStream.flush).toHaveBeenCalledTimes(1);
});
it("keeps Discord progress drafts instead of delivering text-only interim blocks", async () => {
const draftStream = createMockDraftStreamForTest();
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
await params?.dispatcher.sendBlockReply({ text: "on it" });
await params?.replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
await params?.dispatcher.sendFinalReply({ text: "done" });
return { queuedFinal: true, counts: { final: 1, tool: 0, block: 1 } };
});
const ctx = await createAutomaticSourceDeliveryContext({
discordConfig: {
streaming: {
mode: "progress",
progress: {
label: "Shelling",
},
},
},
});
await runProcessDiscordMessage(ctx);
expect(draftStream.update).toHaveBeenCalledWith("Shelling");
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n• tool: exec");
expect(deliverDiscordReply).not.toHaveBeenCalled();
expect(editMessageDiscord).toHaveBeenCalledWith(
"c1",
"preview-1",
{ content: "done" },
expect.objectContaining({ rest: expect.anything() }),
);
});
it("keeps Discord progress lines across assistant boundaries", async () => {
const draftStream = createMockDraftStreamForTest();
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
await params?.replyOptions?.onToolStart?.({ name: "first", phase: "start" });
await params?.replyOptions?.onAssistantMessageStart?.();
await params?.replyOptions?.onToolStart?.({ name: "second", phase: "start" });
return createNoQueuedDispatchResult();
});
const ctx = await createAutomaticSourceDeliveryContext({
discordConfig: {
streaming: {
mode: "progress",
progress: {
label: "Shelling",
},
},
},
});
await runProcessDiscordMessage(ctx);
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n• tool: first");
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n• tool: first\n• tool: second");
expect(draftStream.forceNewMessage).not.toHaveBeenCalled();
});
it("keeps standalone Discord tool progress when partial preview lines are disabled", async () => {
createMockDraftStreamForTest();

View File

@@ -430,6 +430,12 @@ export async function processDiscordMessage(
return;
}
const draftStream = draftPreview.draftStream;
if (draftStream && draftPreview.isProgressMode && info.kind === "block") {
const reply = resolveSendableOutboundReplyParts(payload);
if (!reply.hasMedia && !payload.isError) {
return;
}
}
if (draftStream && isFinal) {
draftPreview.markFinalDeliveryHandled();
const reply = resolveSendableOutboundReplyParts(payload);