import * as fs from "node:fs"; import * as path from "node:path"; import { getRunHistoryPath } from "./paths.js"; export interface RunEntry { agent: string; task: string; ts: number; status: "ok" | "error"; duration: number; exit?: number; } const HISTORY_PATH = getRunHistoryPath(); const ROTATE_READ_THRESHOLD = 1200; const ROTATE_KEEP = 1000; export function recordRun(agent: string, task: string, exitCode: number, durationMs: number): void { try { const entry: RunEntry = { agent, duration: durationMs, status: exitCode === 0 ? "ok" : "error", task: task.slice(0, 200), ts: Math.floor(Date.now() / 1000), ...(exitCode !== 0 ? { exit: exitCode } : {}), }; fs.mkdirSync(path.dirname(HISTORY_PATH), { recursive: true }); fs.appendFileSync(HISTORY_PATH, `${JSON.stringify(entry)}\n`); } catch { // Best-effort: never crash the execution flow for history recording } } export function loadRunsForAgent(agent: string): RunEntry[] { if (!fs.existsSync(HISTORY_PATH)) { return []; } let raw: string; try { raw = fs.readFileSync(HISTORY_PATH, "utf8"); } catch { return []; } let lines = raw .split("\n") .map((line) => line.trim()) .filter((line) => line.length > 0); if (lines.length > ROTATE_READ_THRESHOLD) { lines = lines.slice(-ROTATE_KEEP); try { fs.writeFileSync(HISTORY_PATH, `${lines.join("\n")}\n`, "utf8"); } catch {} } return lines .map((line) => { try { return JSON.parse(line) as RunEntry; } catch { return; } }) .filter((entry): entry is RunEntry => Boolean(entry) && entry.agent === agent) .toReversed(); }