fix(coding-agent/task): fixed task result rendering to show error and success glyphs correctly
- Updated `renderResult` to inspect `isError` on detail-less task results and render an error state with the error glyph. - Replaced success/completed status symbols in agent, review, and result outputs with the shared `status.done` symbol. - Added tests for detail-less task rendering to verify failed results show error and successful results show the done glyph.
This commit is contained in:
@@ -632,7 +632,7 @@ function renderAgentProgress(
|
||||
const indent = prefix ? `${prefix} ` : "";
|
||||
let statusLine: string;
|
||||
if (progress.status === "running") {
|
||||
const bullet = theme.fg("accent", "•");
|
||||
const bullet = theme.styledSymbol("status.done", "text");
|
||||
const name = theme.fg("accent", description ? theme.bold(displayId) : displayId);
|
||||
statusLine = `${indent}${bullet} ${name}`;
|
||||
if (description) {
|
||||
@@ -640,7 +640,11 @@ function renderAgentProgress(
|
||||
statusLine += `${theme.fg("accent", ":")} ${desc}`;
|
||||
}
|
||||
} else {
|
||||
statusLine = `${indent}${theme.fg(iconColor, icon)} ${theme.fg("accent", titlePart)}`;
|
||||
const glyph =
|
||||
progress.status === "completed"
|
||||
? theme.styledSymbol("status.done", "accent")
|
||||
: theme.fg(iconColor, icon);
|
||||
statusLine = `${indent}${glyph} ${theme.fg("accent", titlePart)}`;
|
||||
}
|
||||
|
||||
// Show retry-blocked badge so the parent immediately sees that a child
|
||||
@@ -809,7 +813,7 @@ function renderReviewResult(
|
||||
const verdictColor = summary.overall_correctness === "correct" ? "success" : "error";
|
||||
const isCorrect = summary.overall_correctness === "correct";
|
||||
const verdictIcon = isCorrect
|
||||
? theme.styledSymbol("tool.task", "accent")
|
||||
? theme.styledSymbol("status.done", "accent")
|
||||
: theme.fg(verdictColor, theme.status.error);
|
||||
lines.push(
|
||||
`${continuePrefix} Patch is ${theme.fg(verdictColor, summary.overall_correctness)} ${verdictIcon} ${theme.fg(
|
||||
@@ -916,7 +920,7 @@ function renderAgentResult(
|
||||
: needsWarning
|
||||
? theme.status.warning
|
||||
: success
|
||||
? theme.styledSymbol("tool.task", "accent")
|
||||
? theme.styledSymbol("status.done", "accent")
|
||||
: theme.status.error;
|
||||
const iconColor = needsWarning ? "warning" : success ? "success" : mergeFailed ? "warning" : "error";
|
||||
const statusText = aborted
|
||||
@@ -1074,7 +1078,7 @@ function renderAgentResult(
|
||||
* Render the tool result.
|
||||
*/
|
||||
export function renderResult(
|
||||
result: { content: Array<{ type: string; text?: string }>; details?: TaskToolDetails },
|
||||
result: { content: Array<{ type: string; text?: string }>; details?: TaskToolDetails; isError?: boolean },
|
||||
options: RenderResultOptions,
|
||||
theme: Theme,
|
||||
args?: TaskParams,
|
||||
@@ -1085,18 +1089,25 @@ export function renderResult(
|
||||
|
||||
if (!details) {
|
||||
const text = result.content.find(c => c.type === "text")?.text || "";
|
||||
const header = renderStatusLine(
|
||||
{ iconOverride: theme.styledSymbol("tool.task", "accent"), title: "Task" },
|
||||
theme,
|
||||
);
|
||||
const errored = result.isError === true;
|
||||
const header = errored
|
||||
? renderStatusLine({ icon: "error", title: "Task", description: args?.agent }, theme)
|
||||
: renderStatusLine(
|
||||
{
|
||||
iconOverride: theme.styledSymbol("status.done", "accent"),
|
||||
title: "Task",
|
||||
description: args?.agent,
|
||||
},
|
||||
theme,
|
||||
);
|
||||
return framedBlock(theme, width => ({
|
||||
header,
|
||||
sections: [
|
||||
...(contextSectionRenderer ? [contextSectionRenderer(width)] : []),
|
||||
...(text ? [{ separator: true, lines: [theme.fg("dim", truncateToWidth(text, width))] }] : []),
|
||||
],
|
||||
state: "success",
|
||||
borderColor: "borderMuted",
|
||||
state: errored ? "error" : "success",
|
||||
borderColor: errored ? "error" : "borderMuted",
|
||||
width,
|
||||
}));
|
||||
}
|
||||
@@ -1116,7 +1127,8 @@ export function renderResult(
|
||||
const metaLabel = countLabel ? (agentName ? `${countLabel}: ${agentName}` : countLabel) : agentName;
|
||||
const header = renderStatusLine(
|
||||
{
|
||||
icon,
|
||||
icon: icon === "success" ? undefined : icon,
|
||||
iconOverride: icon === "success" ? theme.styledSymbol("status.done", "accent") : undefined,
|
||||
title: "Task",
|
||||
meta: metaLabel ? [metaLabel] : undefined,
|
||||
},
|
||||
|
||||
@@ -102,3 +102,52 @@ describe("task progress rendering", () => {
|
||||
expect(strippedRow).not.toContain(theme.getSpinnerFrames("status")[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("task result detail-less state", () => {
|
||||
beforeEach(async () => {
|
||||
resetSettingsForTest();
|
||||
await Settings.init({ inMemory: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
resetSettingsForTest();
|
||||
});
|
||||
|
||||
it("renders a validation failure with the error glyph, not a success bullet", async () => {
|
||||
const theme = (await getThemeByName("dark"))!;
|
||||
const options: RenderResultOptions = { expanded: false, isPartial: false };
|
||||
const component = taskToolRenderer.renderResult(
|
||||
{
|
||||
content: [{ type: "text", text: 'Validation failed for tool "task": tasks: Invalid input' }],
|
||||
isError: true,
|
||||
},
|
||||
options,
|
||||
theme,
|
||||
{ agent: "explore", tasks: [] },
|
||||
);
|
||||
const stripped = Bun.stripANSI(component.render(120).join("\n"));
|
||||
|
||||
// A failed task must surface the error glyph and never the "done" bullet.
|
||||
expect(stripped).toContain(theme.status.error);
|
||||
expect(stripped).not.toContain(theme.status.done);
|
||||
expect(stripped).toContain("Task");
|
||||
expect(stripped).toContain("explore");
|
||||
expect(stripped).toContain("Validation failed");
|
||||
});
|
||||
|
||||
it("renders a detail-less success with the accent bullet, not an error glyph", async () => {
|
||||
const theme = (await getThemeByName("dark"))!;
|
||||
const options: RenderResultOptions = { expanded: false, isPartial: false };
|
||||
const component = taskToolRenderer.renderResult(
|
||||
{ content: [{ type: "text", text: "done" }] },
|
||||
options,
|
||||
theme,
|
||||
{ agent: "explore", tasks: [] },
|
||||
);
|
||||
const stripped = Bun.stripANSI(component.render(120).join("\n"));
|
||||
|
||||
expect(stripped).toContain(theme.status.done);
|
||||
expect(stripped).not.toContain(theme.status.error);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user