fix(channels): skip empty progress drafts

This commit is contained in:
Vincent Koc
2026-05-03 16:44:09 -07:00
parent d609859a8b
commit d2ba09b301
5 changed files with 71 additions and 21 deletions

View File

@@ -2794,6 +2794,24 @@ describe("matrix monitor handler draft streaming", () => {
await finish();
});
it("does not create a blank Matrix progress draft when label and lines are disabled", async () => {
const { dispatch } = createStreamingHarness({
streaming: "progress",
previewToolProgressEnabled: false,
accountConfig: {
streaming: { mode: "progress", progress: { label: false, toolProgress: false } },
} as never,
});
const { opts, finish } = await dispatch();
await opts.onItemEvent?.({ progressText: "tool one" });
await opts.onItemEvent?.({ progressText: "tool two" });
expect(opts.suppressDefaultToolProgressMessages).toBe(true);
expect(sendSingleTextMessageMatrixMock).not.toHaveBeenCalled();
await finish();
});
it("keeps partial preview-first finalization on the existing draft when text is unchanged", async () => {
const { dispatch, redactEventMock } = createStreamingHarness({
blockStreamingEnabled: true,

View File

@@ -1504,15 +1504,17 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
if (!draftStream || !progressDraftStreaming) {
return;
}
draftStream.update(
formatChannelProgressDraftText({
entry: progressConfigEntry,
lines: previewToolProgressLines,
seed: progressSeed,
formatLine: formatMatrixToolProgressMarkdownCode,
bullet: "-",
}),
);
const previewText = formatChannelProgressDraftText({
entry: progressConfigEntry,
lines: previewToolProgressLines,
seed: progressSeed,
formatLine: formatMatrixToolProgressMarkdownCode,
bullet: "-",
});
if (!previewText) {
return;
}
draftStream.update(previewText);
};
const progressDraftGate = createChannelProgressDraftGate({
onStart: renderProgressDraft,

View File

@@ -256,13 +256,17 @@ vi.mock("openclaw/plugin-sdk/channel-streaming", () => ({
};
},
formatChannelProgressDraftText: (params: {
entry?: { streaming?: { progress?: { label?: string; maxLines?: number } } };
entry?: { streaming?: { progress?: { label?: string | false; maxLines?: number } } };
lines: string[];
}) =>
[
params.entry?.streaming?.progress?.label ?? "Thinking",
}) => {
const label = params.entry?.streaming?.progress?.label;
return [
label === false ? undefined : (label ?? "Thinking"),
...params.lines.map((line) => `${line}`),
].join("\n"),
]
.filter((line): line is string => Boolean(line))
.join("\n");
},
resolveChannelProgressDraftMaxLines: (entry?: {
streaming?: { progress?: { maxLines?: number } };
}) => entry?.streaming?.progress?.maxLines ?? 8,
@@ -731,6 +735,29 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
expect(capturedReplyOptions?.onItemEvent).toBeDefined();
});
it("does not create a blank Slack progress draft when label and lines are disabled", async () => {
const draftStream = createDraftStreamStub();
createSlackDraftStreamMock.mockReturnValueOnce(draftStream);
mockedSlackStreamingMode = "progress";
mockedSlackDraftMode = "status_final";
mockedDispatchSequence = [];
mockedReplyOptionEvents = [
{ kind: "item", progressText: "tool one" },
{ kind: "item", progressText: "tool two" },
];
await dispatchPreparedSlackMessage(
createPreparedSlackMessage({
accountConfig: {
streaming: { mode: "progress", progress: { label: false, toolProgress: false } },
},
}),
);
expect(capturedReplyOptions?.suppressDefaultToolProgressMessages).toBe(true);
expect(draftStream.update).not.toHaveBeenCalled();
});
it("keeps standalone Slack tool progress when partial preview lines are disabled", async () => {
mockedSlackStreamingMode = "partial";
mockedSlackDraftMode = "replace";

View File

@@ -891,13 +891,15 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
if (!draftStream || streamMode !== "status_final") {
return;
}
draftStream.update(
formatChannelProgressDraftText({
entry: account.config,
lines: previewToolProgressLines,
seed: progressSeed,
}),
);
const previewText = formatChannelProgressDraftText({
entry: account.config,
lines: previewToolProgressLines,
seed: progressSeed,
});
if (!previewText) {
return;
}
draftStream.update(previewText);
hasStreamedMessage = true;
};
const progressDraftGate = createChannelProgressDraftGate({