fix(frontend): update TimelineToolGroup to visually propagate error status with red color and X icon

This commit is contained in:
2026-06-04 12:37:44 -07:00
parent 0439a8dafd
commit febcbf6d2e

View File

@@ -65,7 +65,12 @@ interface Message {
type TimelineEntry = type TimelineEntry =
| { kind: "thought"; text: string } | { kind: "thought"; text: string }
| { kind: "tool"; name: string; status: "running" | "done"; result?: string } | {
kind: "tool";
name: string;
status: "running" | "done" | "error";
result?: string;
}
// A text segment from one round of the assistant's tool loop. // A text segment from one round of the assistant's tool loop.
// Each text SSE event from the server starts a new entry; subsequent // Each text SSE event from the server starts a new entry; subsequent
// streaming chunks for that same round append to the most-recent // streaming chunks for that same round append to the most-recent
@@ -589,6 +594,7 @@ function TimelineToolGroup({
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
const count = entries.length; const count = entries.length;
const allDone = entries.every((e) => e.status === "done"); const allDone = entries.every((e) => e.status === "done");
const hasError = entries.some((e) => e.status === "error");
return ( return (
<div <div
@@ -616,7 +622,9 @@ function TimelineToolGroup({
}} }}
> >
<span style={{ width: 14, display: "flex", justifyContent: "center" }}> <span style={{ width: 14, display: "flex", justifyContent: "center" }}>
{!allDone ? ( {hasError ? (
<span style={{ color: "#ef4444", fontWeight: "bold" }}></span>
) : !allDone ? (
<Loader2 <Loader2
style={{ width: 12, height: 12 }} style={{ width: 12, height: 12 }}
className="animate-spin" className="animate-spin"
@@ -625,8 +633,9 @@ function TimelineToolGroup({
<Wrench style={{ width: 12, height: 12, color: "#2e7d32" }} /> <Wrench style={{ width: 12, height: 12, color: "#2e7d32" }} />
)} )}
</span> </span>
<span style={{ flex: 1 }}> <span style={{ flex: 1, color: hasError ? "#ef4444" : undefined }}>
{category} {count > 1 ? `(x${count})` : ""} {!allDone ? "..." : " ✓"} {category} {count > 1 ? `(x${count})` : ""}
{hasError ? " ✗" : !allDone ? "..." : " ✓"}
</span> </span>
<span <span
style={{ style={{
@@ -1294,6 +1303,14 @@ export function ChatPanel({
// earlier same-named entries. // earlier same-named entries.
let updated = false; let updated = false;
const newTl: TimelineEntry[] = []; const newTl: TimelineEntry[] = [];
const isToolErr =
typeof ev.result === "string" &&
(ev.result.toLowerCase().includes("error") ||
ev.result.toLowerCase().includes("failed") ||
ev.result.toLowerCase().includes("unexpected") ||
ev.result.toLowerCase().includes("not found"));
for (let i = tl.length - 1; i >= 0; i--) { for (let i = tl.length - 1; i >= 0; i--) {
const e = tl[i]; const e = tl[i];
if ( if (
@@ -1304,7 +1321,7 @@ export function ChatPanel({
) { ) {
newTl.unshift({ newTl.unshift({
...e, ...e,
status: "done", status: isToolErr ? "error" : "done",
result: ev.result, result: ev.result,
}); });
updated = true; updated = true;