export function fmtDur(ms: number): string { if (ms < 0) ms = 0; if (ms < 1000) return ms + "ms"; const s = Math.floor(ms / 1000); if (s < 60) return s + "s"; const m = Math.floor(s / 60), rs = s % 60; if (m < 60) return m + "m " + rs + "s"; const h = Math.floor(m / 60), rm = m % 60; return h + "h " + rm + "m"; } export function durClass(ms: number): string { if (ms > 10 * 60 * 1000) return "very-long"; if (ms > 3 * 60 * 1000) return "long"; return ""; } export function fmtTime(ts: number | null): string { if (!ts) return "-"; const d = new Date(ts), now = new Date(); if (d.toDateString() === now.toDateString()) return d.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false }); return d.toLocaleDateString("en-US", { month: "short", day: "numeric" }) + " " + d.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }); } export function statusOf(t: { status: string }): "active" | "queued" | "completed" | "failed" { if (t.status === "processing") return "active"; if (t.status === "queued") return "queued"; if (t.status === "replied" || t.status === "no_reply" || t.status === "skipped") return "completed"; return "failed"; } export function typeClass(t: string): string { if (t.match(/^TOOL CALL/)) return "tool-call"; if (t === "TOOL RESULT") return "tool-result"; if (t === "TEXT") return "text"; if (t.match(/^RESULT/)) return "result"; if (t === "SESSION START") return "session"; if (t === "USER") return "user"; return ""; } export function fmtSessTs(ts: string): string { const p = ts.match(/(\d{2}):(\d{2}):(\d{2})/); return p ? p[1] + ":" + p[2] + ":" + p[3] : ts; } export function formatTypeLabel(type: string): string { if (type.match(/^TOOL CALL/)) return "\u2192 " + type.replace(/^TOOL CALL:\s*/, ""); if (type.match(/^RESULT/)) return "DONE"; if (type === "SESSION START") return "START"; return type; } export function truncate(s: string, max: number): string { return s.length > max ? s.slice(0, max - 3) + "..." : s; } const STALE_MS = 10 * 60 * 1000; export function applyStale(t: { status: string; startedAt: number; processingAt?: number | null; detail: string }): void { const baseline = t.processingAt ?? t.startedAt; if (t.status === "processing" && (Date.now() - baseline) > STALE_MS) { t.status = "stalled"; t.detail = t.detail || "no completion after " + fmtDur(Date.now() - baseline); } }