























@@ -1,6 +1,9 @@
11import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
22import { describe, expect, it, vi } from "vitest";
3-import { createTestDraftStream } from "./draft-stream.test-helpers.js";
3+import {
4+createSequencedTestDraftStream,
5+createTestDraftStream,
6+} from "./draft-stream.test-helpers.js";
47import {
58type ArchivedPreview,
69createLaneTextDeliverer,
@@ -173,7 +176,10 @@ describe("createLaneTextDeliverer", () => {
173176});
174177175178it("primes stop-created previews with final text before editing", async () => {
176-const harness = createHarness({ answerMessageIdAfterStop: 777 });
179+const harness = createHarness({
180+answerMessageIdAfterStop: 777,
181+answerHasStreamedMessage: true,
182+});
177183harness.lanes.answer.lastPartialText = "no";
178184179185const result = await harness.deliverLaneText({
@@ -196,7 +202,10 @@ describe("createLaneTextDeliverer", () => {
196202});
197203198204it("keeps stop-created preview when follow-up final edit fails", async () => {
199-const harness = createHarness({ answerMessageIdAfterStop: 777 });
205+const harness = createHarness({
206+answerMessageIdAfterStop: 777,
207+answerHasStreamedMessage: true,
208+});
200209harness.editPreview.mockRejectedValue(new Error("500: edit failed after stop flush"));
201210202211const result = await harness.deliverLaneText({
@@ -314,6 +323,29 @@ describe("createLaneTextDeliverer", () => {
314323);
315324});
316325326+it("does not create a synthetic preview for final-only text", async () => {
327+const answerStream = createSequencedTestDraftStream(777);
328+const harness = createHarness({
329+answerStream: answerStream as DraftLaneState["stream"],
330+answerHasStreamedMessage: false,
331+});
332+333+const result = await harness.deliverLaneText({
334+laneName: "answer",
335+text: "Final only",
336+payload: { text: "Final only" },
337+infoKind: "final",
338+});
339+340+expect(result.kind).toBe("sent");
341+expect(answerStream.update).not.toHaveBeenCalled();
342+expect(answerStream.materialize).not.toHaveBeenCalled();
343+expect(harness.editPreview).not.toHaveBeenCalled();
344+expect(harness.sendPayload).toHaveBeenCalledWith(
345+expect.objectContaining({ text: "Final only" }),
346+);
347+});
348+317349it("keeps existing preview when final text regresses", async () => {
318350const harness = createHarness({ answerMessageId: 999 });
319351harness.lanes.answer.lastPartialText = "Recovered final answer.";
@@ -485,6 +517,53 @@ describe("createLaneTextDeliverer", () => {
485517expect(harness.markDelivered).toHaveBeenCalledTimes(1);
486518});
487519520+it("does not materialize a native draft for final-only text", async () => {
521+const answerStream = createTestDraftStream({ previewMode: "draft" });
522+answerStream.materialize.mockResolvedValue(321);
523+const harness = createHarness({
524+answerStream: answerStream as DraftLaneState["stream"],
525+answerHasStreamedMessage: false,
526+});
527+528+const result = await harness.deliverLaneText({
529+laneName: "answer",
530+text: "Final only",
531+payload: { text: "Final only" },
532+infoKind: "final",
533+});
534+535+expect(result.kind).toBe("sent");
536+expect(answerStream.update).not.toHaveBeenCalled();
537+expect(answerStream.materialize).not.toHaveBeenCalled();
538+expect(harness.sendPayload).toHaveBeenCalledWith(
539+expect.objectContaining({ text: "Final only" }),
540+);
541+});
542+543+it("does not materialize native draft tool-progress preview before final-only text", async () => {
544+const answerStream = createTestDraftStream({ previewMode: "draft" });
545+answerStream.materialize.mockResolvedValue(321);
546+const harness = createHarness({
547+answerStream: answerStream as DraftLaneState["stream"],
548+answerHasStreamedMessage: false,
549+answerLastPartialText: "Working...\n- tool: exec",
550+});
551+552+const result = await harness.deliverLaneText({
553+laneName: "answer",
554+text: "Final only",
555+payload: { text: "Final only" },
556+infoKind: "final",
557+});
558+559+expect(result.kind).toBe("sent");
560+expect(answerStream.update).not.toHaveBeenCalledWith("Final only");
561+expect(answerStream.materialize).not.toHaveBeenCalled();
562+expect(harness.sendPayload).toHaveBeenCalledWith(
563+expect.objectContaining({ text: "Final only" }),
564+);
565+});
566+488567it("materializes DM draft streaming final when revision changes", async () => {
489568let previewRevision = 3;
490569const answerStream = createTestDraftStream({ previewMode: "draft", messageId: 654 });
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。