mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 20:05:46 -06:00
fix(channels): preserve command progress detail (#94868)
Merged via squash.
Prepared head SHA: 3217f45e61
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
@@ -3229,13 +3229,13 @@ describe("matrix monitor handler draft streaming", () => {
|
||||
expect(editMessageMatrixMock).toHaveBeenCalledWith(
|
||||
"!room:example.org",
|
||||
"$draft1",
|
||||
expect.stringContaining("completed"),
|
||||
expect.stringContaining("Exec"),
|
||||
expect.any(Object),
|
||||
);
|
||||
const recoveredEdit = mockCalls(editMessageMatrixMock, "editMessageMatrix").find(
|
||||
([, eventId, body]) =>
|
||||
eventId === "$draft1" && typeof body === "string" && body.includes("completed"),
|
||||
([, eventId, body]) => eventId === "$draft1" && typeof body === "string",
|
||||
);
|
||||
expect(recoveredEdit?.[2]).not.toContain("completed");
|
||||
expect(recoveredEdit?.[2]).not.toContain("failed");
|
||||
expect(recoveredEdit?.[2]).not.toContain("run openclaw cron -> run jq");
|
||||
});
|
||||
@@ -3293,7 +3293,8 @@ describe("matrix monitor handler draft streaming", () => {
|
||||
([, eventId, body]) =>
|
||||
eventId === "$draft1" && typeof body === "string" && body.includes("completed"),
|
||||
);
|
||||
expect(completedEdit?.[2]).not.toContain("install dependencies");
|
||||
expect(completedEdit).toBeUndefined();
|
||||
expect(singleTextMessageBody()).toContain("install dependencies");
|
||||
});
|
||||
|
||||
it("replaces Matrix patch progress when the patch summary completes", async () => {
|
||||
|
||||
@@ -454,8 +454,8 @@ describe("createMSTeamsReplyDispatcher", () => {
|
||||
});
|
||||
|
||||
const lastUpdate = getStreamMock().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("replaces reasoning progress snapshots in progress mode", async () => {
|
||||
|
||||
@@ -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).not.toContain("install dependencies");
|
||||
expect(lastUpdate?.text).toContain("install dependencies");
|
||||
expect(lastUpdate?.text).not.toContain("completed");
|
||||
expect(lastUpdate?.richMessage).toEqual({
|
||||
html: "<b>Shelling</b><br><b>🛠️ Exec</b> <code>completed</code>",
|
||||
html: "<b>Shelling</b><br><b>🛠️ Exec</b> <code>install dependencies</code>",
|
||||
skip_entity_detection: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ describe("buildChannelProgressDraftLine", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("uses completed status when successful command output has no title", () => {
|
||||
it("uses the tool label when successful command output has no title", () => {
|
||||
const line = buildChannelProgressDraftLine({
|
||||
event: "command-output",
|
||||
phase: "end",
|
||||
@@ -34,10 +34,10 @@ describe("buildChannelProgressDraftLine", () => {
|
||||
|
||||
expect(line).toMatchObject({
|
||||
kind: "command-output",
|
||||
text: "🛠️ completed",
|
||||
detail: "completed",
|
||||
text: "🛠️ Exec",
|
||||
status: "completed",
|
||||
});
|
||||
expect(line?.detail).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps command status and title in raw command progress lines", () => {
|
||||
|
||||
+14
-11
@@ -411,6 +411,15 @@ function resolveCommandProgressCorrelationKey(input: { toolCallId?: string }): s
|
||||
return toolCallId ? `command:${toolCallId}` : undefined;
|
||||
}
|
||||
|
||||
function isTerminalProgressStatus(status: string | undefined): boolean {
|
||||
const normalized = normalizeOptionalLowercaseString(status);
|
||||
return (
|
||||
normalized === "completed" ||
|
||||
normalized === "failed" ||
|
||||
normalized?.startsWith("exit ") === true
|
||||
);
|
||||
}
|
||||
|
||||
function isEmptyReasoningProgressItem(
|
||||
input: Extract<ChannelProgressDraftLineInput, { event: "item" }>,
|
||||
meta: string | undefined,
|
||||
@@ -444,15 +453,6 @@ function buildCommandOutputProgressLine(
|
||||
return line;
|
||||
}
|
||||
if (status === "completed") {
|
||||
if (!line.detail) {
|
||||
const statusLine = {
|
||||
...line,
|
||||
detail: status,
|
||||
text: formatToolAggregate(name, [status], { markdown: options?.markdown }),
|
||||
};
|
||||
setProgressDraftLineCorrelationKey(statusLine, correlationKey);
|
||||
return statusLine;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
if (!line.detail || line.detail === status) {
|
||||
@@ -1153,13 +1153,16 @@ function mergeProgressDraftLineUpdate<TLine extends string | ChannelProgressDraf
|
||||
if (
|
||||
line.kind !== "command-output" ||
|
||||
!line.status ||
|
||||
line.status === "completed" ||
|
||||
(line.detail && line.detail !== line.status)
|
||||
) {
|
||||
return line;
|
||||
}
|
||||
const previousDetail = previous.detail?.trim();
|
||||
if (!previousDetail || previousDetail === previous.status) {
|
||||
if (
|
||||
!previousDetail ||
|
||||
previousDetail === previous.status ||
|
||||
isTerminalProgressStatus(previous.status)
|
||||
) {
|
||||
return line;
|
||||
}
|
||||
const replacement = {
|
||||
|
||||
@@ -601,16 +601,16 @@ describe("channel-streaming", () => {
|
||||
expect(updated[0]).toMatchObject({
|
||||
id: "tool:call-1-output",
|
||||
kind: "command-output",
|
||||
detail: "completed",
|
||||
detail: "install dependencies",
|
||||
status: "completed",
|
||||
text: "🛠️ completed",
|
||||
text: "🛠️ install dependencies",
|
||||
});
|
||||
expect(
|
||||
formatChannelProgressDraftText({
|
||||
lines: updated,
|
||||
entry: { streaming: { progress: { label: false } } },
|
||||
}),
|
||||
).toBe("🛠️ completed");
|
||||
).toBe("🛠️ install dependencies");
|
||||
|
||||
const recoveredItemLine = buildChannelProgressDraftLine({
|
||||
event: "item",
|
||||
@@ -632,19 +632,20 @@ describe("channel-streaming", () => {
|
||||
if (!recoveredItemLine || !recoveredCommandLine) {
|
||||
throw new Error("expected recovered command progress lines");
|
||||
}
|
||||
expect(
|
||||
mergeChannelProgressDraftLine([recoveredItemLine], recoveredCommandLine, {
|
||||
maxLines: 4,
|
||||
}),
|
||||
).toMatchObject([
|
||||
const recoveredUpdated = mergeChannelProgressDraftLine(
|
||||
[recoveredItemLine],
|
||||
recoveredCommandLine,
|
||||
{ maxLines: 4 },
|
||||
);
|
||||
expect(recoveredUpdated).toMatchObject([
|
||||
{
|
||||
id: "command-2",
|
||||
kind: "command-output",
|
||||
detail: "completed",
|
||||
status: "completed",
|
||||
text: "🛠️ completed",
|
||||
text: "🛠️ Bash",
|
||||
},
|
||||
]);
|
||||
expect(recoveredUpdated[0]).not.toHaveProperty("detail");
|
||||
});
|
||||
|
||||
it("starts progress drafts after five seconds or a second work event", async () => {
|
||||
|
||||
Reference in New Issue
Block a user