import path from "node:path"; import { STATE_DIR_CLAWDBOT } from "../config/paths.js"; import { loadJsonFile, saveJsonFile } from "../infra/json-file.js"; import type { SubagentRunRecord } from "./subagent-registry.js"; export type PersistedSubagentRegistryVersion = 1; type PersistedSubagentRegistry = { version: 1; runs: Record; }; const REGISTRY_VERSION = 1 as const; type PersistedSubagentRunRecord = Omit & { announceHandled?: boolean; }; export function resolveSubagentRegistryPath(): string { return path.join(STATE_DIR_CLAWDBOT, "subagents", "runs.json"); } export function loadSubagentRegistryFromDisk(): Map { const pathname = resolveSubagentRegistryPath(); const raw = loadJsonFile(pathname); if (!raw || typeof raw !== "object") return new Map(); const record = raw as Partial; if (record.version !== REGISTRY_VERSION) return new Map(); const runsRaw = record.runs; if (!runsRaw || typeof runsRaw !== "object") return new Map(); const out = new Map(); for (const [runId, entry] of Object.entries(runsRaw)) { if (!entry || typeof entry !== "object") continue; const typed = entry as PersistedSubagentRunRecord; if (!typed.runId || typeof typed.runId !== "string") continue; // Back-compat: map legacy announce fields into cleanup fields. const announceCompletedAt = typeof typed.announceCompletedAt === "number" ? typed.announceCompletedAt : undefined; const cleanupCompletedAt = typeof typed.cleanupCompletedAt === "number" ? typed.cleanupCompletedAt : announceCompletedAt; const cleanupHandled = typeof typed.cleanupHandled === "boolean" ? typed.cleanupHandled : Boolean(typed.announceHandled ?? announceCompletedAt ?? cleanupCompletedAt); const announceHandled = typeof typed.announceHandled === "boolean" ? typed.announceHandled : Boolean(announceCompletedAt); out.set(runId, { ...typed, announceCompletedAt, announceHandled, cleanupCompletedAt, cleanupHandled, }); } return out; } export function saveSubagentRegistryToDisk(runs: Map) { const pathname = resolveSubagentRegistryPath(); const serialized: Record = {}; for (const [runId, entry] of runs.entries()) { const { announceHandled: _ignored, ...persisted } = entry; serialized[runId] = persisted; } const out: PersistedSubagentRegistry = { version: REGISTRY_VERSION, runs: serialized, }; saveJsonFile(pathname, out); }