fix: seal mattermost preview finalization

This commit is contained in:
Muhammed Mukhthar CM
2026-04-20 17:52:41 +05:30
committed by GitHub
parent 6ed6a8a3b2
commit 9301398f8b
3 changed files with 13 additions and 2 deletions

View File

@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
- Plugins/tasks: add a detached runtime registration contract so plugin executors can own detached task lifecycle and cancellation without reaching into core task internals. (#68915) Thanks @mbelinky.
- Terminal/logging: optimize `sanitizeForLog()` by replacing the iterative control-character stripping loop with a single regex pass while preserving the existing ANSI-first sanitization behavior. (#67205) Thanks @bulutmuf.
- QA/CI: make `openclaw qa suite` and `openclaw qa telegram` fail by default when scenarios fail, add `--allow-failures` for artifact-only runs, and tighten live-lane defaults for CI automation. (#69122) Thanks @joshavant.
- Mattermost: stream thinking, tool activity, and partial reply text into a single draft preview post that finalizes in place when safe. (#47838) thanks @ninjaa.
### Fixes
@@ -256,7 +257,6 @@ Docs: https://docs.openclaw.ai
- OpenAI Codex/models: add forward-compat support for `gpt-5.4-pro`, including Codex pricing/limits and list/status visibility before the upstream catalog catches up. (#66453) Thanks @jepson-liu.
- Telegram/forum topics: surface human topic names in agent context, prompt metadata, and plugin hook metadata by learning names from Telegram forum service messages. (#65973) Thanks @ptahdunbar.
- Mattermost: stream thinking, tool activity, and partial reply text into a single draft preview post that finalizes in place when safe. (#47838) thanks @ninjaa.
### Fixes

View File

@@ -66,6 +66,7 @@ function createDraftStreamMock(postId: string | undefined = "preview-post-1") {
flush: vi.fn(async () => {}),
postId: vi.fn(() => postId),
clear: vi.fn(async () => {}),
stop: vi.fn(async () => {}),
};
}
@@ -315,6 +316,10 @@ describe("deliverMattermostReplyWithDraftPreview", () => {
"preview-post-1",
expect.objectContaining({ message: "Final answer" }),
);
expect(draftStream.stop).toHaveBeenCalledTimes(1);
expect(draftStream.stop.mock.invocationCallOrder[0]).toBeLessThan(
updateMattermostPostSpy.mock.invocationCallOrder[0] ?? Number.POSITIVE_INFINITY,
);
expect(deliverFinal).not.toHaveBeenCalled();
expect(draftStream.clear).not.toHaveBeenCalled();
});

View File

@@ -274,7 +274,10 @@ type MattermostDraftPreviewDeliverParams = {
payload: ReplyPayload;
info: { kind: "tool" | "block" | "final" };
client: MattermostClient;
draftStream: Pick<ReturnType<typeof createMattermostDraftStream>, "flush" | "postId" | "clear">;
draftStream: Pick<
ReturnType<typeof createMattermostDraftStream>,
"flush" | "postId" | "clear" | "stop"
>;
effectiveReplyToId?: string;
resolvePreviewFinalText: (text?: string) => string | undefined;
previewState: MattermostDraftPreviewState;
@@ -310,6 +313,9 @@ export async function deliverMattermostReplyWithDraftPreview(
})
) {
try {
// Seal the preview before the final edit so late draft events cannot
// patch over the finalized visible message.
await params.draftStream.stop();
await updateMattermostPost(params.client, previewPostId, {
message: previewFinalText,
});