import type { TaskLogEntry } from "@fusion/core"; export interface TimingEvent { timestamp: string; durationMs?: number; summary: string; } function summarizeTimingLabel(entry: TaskLogEntry): string { const timingText = entry.action || entry.outcome || ""; const stripped = timingText .replace(/^\[timing\]\s*/i, "") .replace(/^\[[^\]]+\]\s*/i, "") .replace(/\s+in\s+\d+(?:\.\d+)?ms\b/i, "") .replace(/\s+after\s+\d+(?:\.\d+)?ms\b/i, "") .trim(); return stripped || "Timing event"; } export function extractTimingEvents(logEntries: TaskLogEntry[]): TimingEvent[] { return logEntries .filter((entry) => { const actionText = typeof entry.action === "string" ? entry.action : ""; const outcomeText = typeof entry.outcome === "string" ? entry.outcome : ""; return actionText.includes("[timing]") || outcomeText.includes("[timing]"); }) .map((entry) => { const haystack = `${entry.action ?? ""}\n${entry.outcome ?? ""}`; const durationMatch = haystack.match(/(\d+(?:\.\d+)?)ms\b/i); const durationMs = durationMatch ? Number(durationMatch[1]) : undefined; return { timestamp: entry.timestamp, durationMs: Number.isFinite(durationMs) ? durationMs : undefined, summary: summarizeTimingLabel(entry), }; }); } export function getTimedDurationMs(logEntries: TaskLogEntry[] | undefined): number | null { if (!logEntries || logEntries.length === 0) return null; let total = 0; let counted = 0; for (const event of extractTimingEvents(logEntries)) { if (typeof event.durationMs !== "number") continue; total += event.durationMs; counted += 1; } return counted > 0 ? total : null; }