






















@@ -88,6 +88,81 @@ describe("buildEmbeddedRunPayloads tool-error warnings", () => {
8888expectSinglePayloadText(payloads, "Fixed.");
8989});
909091+it("delivers only the final assistant answer when accumulated text includes pre-tool progress", () => {
92+const payloads = buildPayloads({
93+assistantTexts: ["I'll inspect that first.", "Done."],
94+lastAssistant: {
95+role: "assistant",
96+stopReason: "stop",
97+content: [
98+{
99+type: "text",
100+text: "Done.",
101+textSignature: JSON.stringify({
102+v: 1,
103+id: "item_final",
104+phase: "final_answer",
105+}),
106+},
107+],
108+} as AssistantMessage,
109+});
110+111+expectSinglePayloadText(payloads, "Done.");
112+});
113+114+it("does not replay raw-looking accumulated tool output when final answer text is available", () => {
115+const payloads = buildPayloads({
116+assistantTexts: [
117+"/root/openclaw/src/gateway/protocol/schema/protocol-schemas.ts:181: PluginControlUiDescriptorSchema,",
118+"The schema export is fixed.",
119+],
120+lastAssistant: {
121+role: "assistant",
122+stopReason: "stop",
123+content: [
124+{
125+type: "text",
126+text: "The schema export is fixed.",
127+textSignature: JSON.stringify({
128+v: 1,
129+id: "item_final",
130+phase: "final_answer",
131+}),
132+},
133+],
134+} as AssistantMessage,
135+});
136+137+expectSinglePayloadText(payloads, "The schema export is fixed.");
138+});
139+140+it("ignores accumulated internal/status text after the final answer", () => {
141+const payloads = buildPayloads({
142+assistantTexts: [
143+"Done.",
144+"Background task done: Context engine turn maintenance. Rewrote 0 transcript entries and freed 0 bytes.",
145+],
146+lastAssistant: {
147+role: "assistant",
148+stopReason: "stop",
149+content: [
150+{
151+type: "text",
152+text: "Done.",
153+textSignature: JSON.stringify({
154+v: 1,
155+id: "item_final",
156+phase: "final_answer",
157+}),
158+},
159+],
160+} as AssistantMessage,
161+});
162+163+expectSinglePayloadText(payloads, "Done.");
164+});
165+91166it("surfaces concise exec tool errors when verbose mode is off", () => {
92167const payloads = buildPayloads({
93168lastToolError: { toolName: "exec", error: "command failed" },
@@ -283,6 +358,32 @@ describe("buildEmbeddedRunPayloads tool-error warnings", () => {
283358expect(payloads[0]?.mediaUrls).toEqual(["/tmp/reply-image.png"]);
284359});
285360361+it("keeps media directives when collapsing accumulated pre-tool text to the final answer", () => {
362+const payloads = buildPayloads({
363+assistantTexts: ["Preparing the image...", "Attached image"],
364+lastAssistant: {
365+role: "assistant",
366+stopReason: "stop",
367+content: [
368+{
369+type: "text",
370+text: "MEDIA:/tmp/reply-image.png\nAttached image",
371+textSignature: JSON.stringify({
372+v: 1,
373+id: "item_final",
374+phase: "final_answer",
375+}),
376+},
377+],
378+} as AssistantMessage,
379+});
380+381+expect(payloads).toHaveLength(1);
382+expect(payloads[0]?.text).toBe("Attached image");
383+expect(payloads[0]?.mediaUrl).toBe("/tmp/reply-image.png");
384+expect(payloads[0]?.mediaUrls).toEqual(["/tmp/reply-image.png"]);
385+});
386+286387it("uses raw final assistant text when visible-text extraction removed a media-only directive line", () => {
287388const payloads = buildPayloads({
288389lastAssistant: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。