"use client"; import { humanToolName, taskPhaseLabel, TaskEvent } from "@/lib/task-types"; const PHASES = [ { id: "context", label: "Context" }, { id: "triage", label: "Plan" }, { id: "connect", label: "Connect" }, { id: "diagnose", label: "Diagnose" }, { id: "reason", label: "Analyze" }, { id: "report", label: "Report" }, ] as const; function phaseIndex(phase: string | undefined): number { if (!phase) return -1; return PHASES.findIndex((p) => p.id === phase); } function derivePhase(events: TaskEvent[], taskStatus?: string): string { if (taskStatus === "succeeded" || taskStatus === "waiting_approval") return "report"; for (let i = events.length - 1; i >= 0; i--) { const p = events[i].payload?.phase as string | undefined; if (p) return p; const k = events[i].kind; if (k === "context" || k === "rule") return "context"; if (k === "triage") return "triage"; if (k === "connect") return "connect"; if (k === "diagnose" || k === "tool_start" || k === "tool_call") return "diagnose"; if (k === "reason") return "reason"; if (k === "report") return "report"; } return "context"; } function activeTool(events: TaskEvent[]): TaskEvent | null { for (let i = events.length - 1; i >= 0; i--) { const ev = events[i]; if (ev.kind === "tool_start") return ev; if (ev.kind === "tool_call" || ev.kind === "error") return null; } return null; } export default function TaskLiveStatus({ events, taskStatus, }: { events: TaskEvent[]; taskStatus: string; }) { const running = taskStatus === "running" || taskStatus === "queued"; const currentPhase = derivePhase(events, taskStatus); const currentIdx = phaseIndex(currentPhase); const active = activeTool(events); const lastProgress = [...events].reverse().find((e) => e.kind === "progress"); const devices = Array.from( new Set( events .map((e) => e.payload?.device) .filter((device): device is string => typeof device === "string" && !!device) ) ); const nowDoing = active ? `${active.payload?.device || "Device"}: ${humanToolName(String(active.payload?.tool || "diagnostic"))}` : taskPhaseLabel(taskStatus, events); return (

Task status

{nowDoing}

{running && ( In progress )}
{PHASES.map((phase, idx) => { const done = !running && currentIdx >= 0 ? idx <= currentIdx : idx < currentIdx; const activePhase = idx === currentIdx && running; return (
{phase.label}
); })}
{devices.length > 0 && (
{devices.map((device) => ( {device} ))}
)} {(lastProgress?.message || active) && (
{lastProgress?.message && (

{lastProgress.message}

)} {active && (
Running now
{(active.payload?.device as string) || "?"} - {humanToolName((active.payload?.tool as string) || "tool")}
{typeof active.payload?.command === "string" && (
                  $ {active.payload.command}
                
)}
)}
)}
); } function eventTime(ev: TaskEvent) { const raw = ev.ts || ev.created_at; if (!raw) return ""; try { return new Date(raw).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }); } catch { return ""; } } export function TimelineEvent({ ev }: { ev: TaskEvent }) { const p = ev.payload || {}; const isError = ev.kind === "error" || p.status === "error" || p.ok === false; const isRunning = p.status === "running" || ev.kind === "tool_start"; const isDone = p.ok === true || p.status === "done"; const border = isError ? "border-l-2 border-l-bad" : isRunning ? "border-l-2 border-l-accent animate-pulse" : isDone ? "border-l-2 border-l-good" : "border-l-2 border-l-transparent"; return (
{isError ? "!" : isRunning ? ">" : "-"} {ev.kind.replace("_", " ")} {eventTime(ev) && {eventTime(ev)}}
{typeof p.phase === "string" && ( {p.phase} )} {p.ok === false && fail} {isRunning && running…}
{ev.message &&
{ev.message}
} {typeof p.tool === "string" && (
{typeof p.device === "string" ? `${p.device} - ` : ""} {humanToolName(p.tool)}
)} {typeof p.command === "string" && (
Command
            $ {p.command}
          
)} {typeof p.output === "string" && p.output && (
Evidence output
            {p.output}
          
)} {typeof p.error === "string" && (
{p.error}
)}
); }