diff --git a/extensions/discord/src/monitor/message-handler.process.test.ts b/extensions/discord/src/monitor/message-handler.process.test.ts
index 09473c9bc56a..9bd5fbbd5ec5 100644
--- a/extensions/discord/src/monitor/message-handler.process.test.ts
+++ b/extensions/discord/src/monitor/message-handler.process.test.ts
@@ -3057,8 +3057,8 @@ describe("processDiscordMessage draft streaming", () => {
await runProcessDiscordMessage(ctx);
const lastUpdate = draftStream.update.mock.calls.at(-1)?.[0];
- expect(lastUpdate).toContain("completed");
- expect(lastUpdate).not.toContain("install dependencies");
+ expect(lastUpdate).toContain("install dependencies");
+ expect(lastUpdate).not.toContain("completed");
});
it("drops later tool warning finals after progress preview final replies", async () => {
diff --git a/extensions/slack/src/progress-blocks.test.ts b/extensions/slack/src/progress-blocks.test.ts
index bcf83246be21..45aec9dd3b0f 100644
--- a/extensions/slack/src/progress-blocks.test.ts
+++ b/extensions/slack/src/progress-blocks.test.ts
@@ -134,7 +134,7 @@ describe("buildSlackProgressDraftBlocks", () => {
],
});
- expectLegacyLineBlock(blocks?.[1], "• *Exec*", "command finished · completed");
+ expectLegacyLineBlock(blocks?.[1], "• *Exec*", "command finished");
expectLegacyLineBlock(blocks?.[2], "• *Exec*", "command failed · exit 1");
});
@@ -243,7 +243,7 @@ describe("native Slack progress stream chunks", () => {
}),
).toEqual([
planUpdate("Shelling..."),
- taskUpdate("exec_1", "Exec — command finished · completed", "complete"),
+ taskUpdate("exec_1", "Exec — command finished", "complete"),
taskUpdate("exec_2", "Exec — command failed · exit 1", "error"),
]);
});
diff --git a/extensions/slack/src/progress-blocks.ts b/extensions/slack/src/progress-blocks.ts
index 6ad35e44f1d8..6b911b6d7ca7 100644
--- a/extensions/slack/src/progress-blocks.ts
+++ b/extensions/slack/src/progress-blocks.ts
@@ -59,7 +59,12 @@ function compactChunkText(value: string): string {
}
function lineDetailParts(line: ChannelProgressDraftLine): string[] {
- return [line.detail, line.status && !line.detail?.includes(line.status) ? line.status : undefined]
+ return [
+ line.detail,
+ line.status && line.status !== "completed" && !line.detail?.includes(line.status)
+ ? line.status
+ : undefined,
+ ]
.map((part) => part?.trim())
.filter((part): part is string => Boolean(part));
}
diff --git a/extensions/telegram/src/bot-message-dispatch.test.ts b/extensions/telegram/src/bot-message-dispatch.test.ts
index c07274610752..57672686829b 100644
--- a/extensions/telegram/src/bot-message-dispatch.test.ts
+++ b/extensions/telegram/src/bot-message-dispatch.test.ts
@@ -2625,10 +2625,10 @@ describe("dispatchTelegramMessage draft streaming", () => {
});
const lastUpdate = answerDraftStream.updatePreview.mock.calls.at(-1)?.[0];
- expect(lastUpdate?.text).toContain("completed");
expect(lastUpdate?.text).toContain("install dependencies");
+ expect(lastUpdate?.text).not.toContain("completed");
expect(lastUpdate?.richMessage).toEqual({
- html: "Shelling
🛠️ Exec install dependencies completed",
+ html: "Shelling
🛠️ Exec install dependencies",
skip_entity_detection: true,
});
});
diff --git a/extensions/telegram/src/bot-message-dispatch.ts b/extensions/telegram/src/bot-message-dispatch.ts
index f8d5a5497805..637481870168 100644
--- a/extensions/telegram/src/bot-message-dispatch.ts
+++ b/extensions/telegram/src/bot-message-dispatch.ts
@@ -458,7 +458,7 @@ function renderTelegramProgressLine(line: ChannelProgressDraftCompositorLine): s
parts.push(renderTelegramProgressStringLine(text));
}
}
- if (line.status && line.status !== line.detail) {
+ if (line.status && line.status !== "completed" && line.status !== line.detail) {
parts.push(`${escapeTelegramProgressHtml(line.status)}`);
}
return parts.join(" ");
diff --git a/src/channels/streaming.test.ts b/src/channels/streaming.test.ts
index 802a231b590b..10e67e373d06 100644
--- a/src/channels/streaming.test.ts
+++ b/src/channels/streaming.test.ts
@@ -2,6 +2,44 @@ import { describe, expect, it } from "vitest";
import { buildChannelProgressDraftLine } from "./streaming.js";
describe("buildChannelProgressDraftLine", () => {
+ it("omits generic completed status from successful command output with title", () => {
+ const line = buildChannelProgressDraftLine(
+ {
+ event: "command-output",
+ toolCallId: "exec-1",
+ phase: "end",
+ title: "pwd",
+ name: "exec",
+ exitCode: 0,
+ },
+ { commandText: "raw" },
+ );
+
+ expect(line).toMatchObject({
+ kind: "command-output",
+ id: "exec-1",
+ text: "🛠️ pwd",
+ detail: "pwd",
+ status: "completed",
+ });
+ });
+
+ it("uses the tool label when successful command output has no title", () => {
+ const line = buildChannelProgressDraftLine({
+ event: "command-output",
+ phase: "end",
+ name: "exec",
+ exitCode: 0,
+ });
+
+ expect(line).toMatchObject({
+ kind: "command-output",
+ text: "🛠️ Exec",
+ status: "completed",
+ });
+ expect(line?.detail).toBeUndefined();
+ });
+
it("keeps command status and title in raw command progress lines", () => {
const line = buildChannelProgressDraftLine(
{
diff --git a/src/channels/streaming.ts b/src/channels/streaming.ts
index 80759a961eb3..f9f325b2379f 100644
--- a/src/channels/streaming.ts
+++ b/src/channels/streaming.ts
@@ -443,6 +443,9 @@ function buildCommandOutputProgressLine(
if (!line || !status) {
return line;
}
+ if (status === "completed") {
+ return line;
+ }
if (!line.detail || line.detail === status) {
const statusLine = {
...line,
@@ -1055,11 +1058,14 @@ function getProgressDraftLineText(line: string | ChannelProgressDraftLine): stri
const label = line.label.trim();
const detail = line.detail?.trim();
const status = line.status?.trim();
+ const displayStatus = status === "completed" ? undefined : status;
if (detail) {
const compactCommandLine =
line.toolName === "exec" || line.toolName === "bash" || line.toolName === "shell";
- if (line.kind === "command-output" && status && detail !== status) {
- const outputDetail = detail.startsWith(`${status};`) ? detail : `${status}; ${detail}`;
+ if (line.kind === "command-output" && displayStatus && detail !== displayStatus) {
+ const outputDetail = detail.startsWith(`${displayStatus};`)
+ ? detail
+ : `${displayStatus}; ${detail}`;
if (compactCommandLine) {
return `${prefix}${outputDetail}`;
}
@@ -1070,11 +1076,11 @@ function getProgressDraftLineText(line: string | ChannelProgressDraftLine): stri
}
return `${prefix}${detail}`;
}
- if (status) {
+ if (displayStatus) {
if (label) {
- return `${prefix}${label}: ${status}`;
+ return `${prefix}${label}: ${displayStatus}`;
}
- return `${prefix}${status}`;
+ return `${prefix}${displayStatus}`;
}
const text = line.text.trim();
if (!icon && text && text !== label) {
diff --git a/src/plugin-sdk/channel-streaming.test.ts b/src/plugin-sdk/channel-streaming.test.ts
index 378b2507b85c..7fff4a7b4ac9 100644
--- a/src/plugin-sdk/channel-streaming.test.ts
+++ b/src/plugin-sdk/channel-streaming.test.ts
@@ -601,10 +601,16 @@ describe("channel-streaming", () => {
expect(updated[0]).toMatchObject({
id: "tool:call-1-output",
kind: "command-output",
- status: "completed",
detail: "install dependencies",
- text: "🛠️ completed; install dependencies",
+ status: "completed",
+ text: "🛠️ install dependencies",
});
+ expect(
+ formatChannelProgressDraftText({
+ lines: updated,
+ entry: { streaming: { progress: { label: false } } },
+ }),
+ ).toBe("🛠️ install dependencies");
const recoveredItemLine = buildChannelProgressDraftLine({
event: "item",
@@ -634,9 +640,9 @@ describe("channel-streaming", () => {
{
id: "command-2",
kind: "command-output",
- status: "completed",
detail: "install dependencies failed",
- text: "🛠️ completed; install dependencies failed",
+ status: "completed",
+ text: "🛠️ install dependencies failed",
},
]);
});