import "./chunk-PZ5AY32C.js";

// src/maintenance/dreams-ledger.ts
import path from "path";
import { appendFile, lstat, mkdir, readdir, readFile } from "fs/promises";
function dreamsLedgerPath(memoryDir) {
  return path.join(memoryDir, "state", "dreams-ledger.jsonl");
}
async function appendDreamsLedgerEntry(memoryDir, entry) {
  const ledgerPath = dreamsLedgerPath(memoryDir);
  await mkdir(path.dirname(ledgerPath), { recursive: true });
  await appendFile(ledgerPath, JSON.stringify(entry) + "\n", "utf-8");
}
async function readDreamsLedgerEntries(memoryDir) {
  const ledgerPath = dreamsLedgerPath(memoryDir);
  let raw;
  try {
    raw = await readFile(ledgerPath, "utf-8");
  } catch (err) {
    const code = err.code;
    if (code === "ENOENT") return [];
    throw err;
  }
  const entries = [];
  for (const line of raw.split("\n")) {
    const trimmed = line.trim();
    if (!trimmed) continue;
    try {
      const parsed = JSON.parse(trimmed);
      if (typeof parsed !== "object" || parsed === null) continue;
      if (typeof parsed.phase === "string" && (parsed.phase === "lightSleep" || parsed.phase === "rem" || parsed.phase === "deepSleep") && typeof parsed.startedAt === "string" && typeof parsed.completedAt === "string" && typeof parsed.durationMs === "number" && typeof parsed.itemsProcessed === "number") {
        entries.push({
          schemaVersion: 1,
          startedAt: parsed.startedAt,
          completedAt: parsed.completedAt,
          durationMs: parsed.durationMs,
          phase: parsed.phase,
          itemsProcessed: parsed.itemsProcessed,
          dryRun: parsed.dryRun === true,
          trigger: parsed.trigger === "manual" ? "manual" : "scheduled",
          notes: typeof parsed.notes === "string" ? parsed.notes : void 0
        });
      }
    } catch {
    }
  }
  return entries;
}
var ALL_PHASES = ["lightSleep", "rem", "deepSleep"];
var MAX_WINDOW_HOURS = 24 * 365 * 100;
function normalizeDreamsStatusWindowHours(value, fallback = 24) {
  const raw = value === void 0 || value === null ? fallback : value;
  if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw) || raw < 1 || raw > MAX_WINDOW_HOURS) {
    throw new RangeError(`windowHours must be a positive integer no greater than ${MAX_WINDOW_HOURS}`);
  }
  return raw;
}
async function getDreamsStatus(memoryDir, windowHours = 24, now = /* @__PURE__ */ new Date()) {
  const boundedWindowHours = normalizeDreamsStatusWindowHours(windowHours);
  const nowMs = now.getTime();
  if (!Number.isFinite(nowMs)) {
    throw new RangeError("now must be a valid Date");
  }
  const windowMs = boundedWindowHours * 60 * 60 * 1e3;
  const windowStart = new Date(nowMs - windowMs);
  const windowEnd = now;
  if (!Number.isFinite(windowStart.getTime())) {
    throw new RangeError("windowHours produces an invalid status window");
  }
  const entries = await readDreamsLedgerEntries(memoryDir);
  const windowEntries = entries.filter((e) => {
    const ts = Date.parse(e.completedAt);
    return Number.isFinite(ts) && ts >= windowStart.getTime() && ts < windowEnd.getTime();
  });
  const statusMap = /* @__PURE__ */ new Map();
  for (const phase of ALL_PHASES) {
    statusMap.set(phase, {
      phase,
      runCount: 0,
      totalDurationMs: 0,
      totalItemsProcessed: 0,
      lastRunAt: null,
      lastDurationMs: null
    });
  }
  for (const entry of windowEntries) {
    const status = statusMap.get(entry.phase);
    if (!status) continue;
    status.runCount += 1;
    status.totalDurationMs += entry.durationMs;
    status.totalItemsProcessed += entry.itemsProcessed;
    if (status.lastRunAt === null || Date.parse(entry.completedAt) > Date.parse(status.lastRunAt)) {
      status.lastRunAt = entry.completedAt;
      status.lastDurationMs = entry.durationMs;
    }
  }
  return {
    windowStart: windowStart.toISOString(),
    windowEnd: windowEnd.toISOString(),
    phases: {
      lightSleep: statusMap.get("lightSleep"),
      rem: statusMap.get("rem"),
      deepSleep: statusMap.get("deepSleep")
    }
  };
}
async function recordDreamsPhaseRun(options) {
  const startedAt = options.startedAt ?? (/* @__PURE__ */ new Date()).toISOString();
  const completedAt = options.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
  const durationMs = Math.max(0, Date.parse(completedAt) - Date.parse(startedAt));
  const ledgerEntry = {
    schemaVersion: 1,
    startedAt,
    completedAt,
    durationMs: Number.isFinite(durationMs) ? durationMs : 0,
    phase: options.phase,
    itemsProcessed: Math.max(0, Math.floor(options.itemsProcessed)),
    dryRun: options.dryRun === true,
    trigger: options.trigger,
    notes: options.notes
  };
  if (!ledgerEntry.dryRun) {
    await appendDreamsLedgerEntry(options.memoryDir, ledgerEntry);
  }
  return ledgerEntry;
}
async function runDreamsPhase(options, governanceRunner, phaseRunner) {
  const { memoryDir, phase, dryRun = false } = options;
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
  let itemsProcessed = 0;
  let notes;
  if (phase === "lightSleep") {
    if (!dryRun) {
      if (!phaseRunner) {
        throw new Error("light-sleep manual runs require a phase runner");
      }
      const result = await phaseRunner({ memoryDir, phase });
      itemsProcessed = Math.max(0, Math.floor(result.itemsProcessed));
      notes = result.notes ?? `scored ${itemsProcessed} memories`;
    } else {
      try {
        const ledgerPath = path.join(memoryDir, "state", "observation-ledger", "rebuilt-observations.jsonl");
        let raw = "";
        try {
          raw = await readFile(ledgerPath, "utf-8");
        } catch {
        }
        const lines = raw.split("\n").filter((l) => l.trim().length > 0);
        const cutoff = Date.now() - 24 * 60 * 60 * 1e3;
        itemsProcessed = lines.filter((line) => {
          try {
            const obj = JSON.parse(line);
            const timestamp = typeof obj.ts === "string" ? obj.ts : typeof obj.timestamp === "string" ? obj.timestamp : null;
            if (!timestamp) return false;
            const ms = Date.parse(timestamp);
            return Number.isFinite(ms) && ms >= cutoff;
          } catch {
            return false;
          }
        }).length;
        notes = `dry-run: would score ${itemsProcessed} observation entries`;
      } catch (err) {
        throw new Error(`light-sleep scan failed: ${err instanceof Error ? err.message : String(err)}`);
      }
    }
  } else if (phase === "rem") {
    if (!dryRun) {
      if (!phaseRunner) {
        throw new Error("REM manual runs require a phase runner");
      }
      const result = await phaseRunner({ memoryDir, phase });
      itemsProcessed = Math.max(0, Math.floor(result.itemsProcessed));
      notes = result.notes ?? `REM pass assessed ${itemsProcessed} memories`;
    } else {
      try {
        const stateFilePath = path.join(memoryDir, "state", "semantic-consolidation-last-run.json");
        let lastRunAt = null;
        try {
          const stateRaw = await readFile(stateFilePath, "utf-8");
          const stateData = JSON.parse(stateRaw);
          lastRunAt = stateData.lastRunAt ?? null;
        } catch {
        }
        const memFiles = await listMemoryFiles(memoryDir);
        itemsProcessed = memFiles.length;
        notes = `dry-run: ${itemsProcessed} memories would enter REM consolidation pass${lastRunAt ? ` (last run: ${lastRunAt})` : " (never run)"}`;
      } catch (err) {
        throw new Error(`REM scan failed: ${err instanceof Error ? err.message : String(err)}`);
      }
    }
  } else {
    if (governanceRunner) {
      try {
        const result = await governanceRunner({ memoryDir, dryRun });
        itemsProcessed = result.scannedMemories;
        notes = result.notes ?? (dryRun ? `dry-run: ${result.appliedActionCount} actions proposed` : `${result.appliedActionCount} actions applied`);
      } catch (err) {
        throw new Error(`deep-sleep governance run failed: ${err instanceof Error ? err.message : String(err)}`);
      }
    } else {
      const memFiles = await listMemoryFiles(memoryDir);
      itemsProcessed = memFiles.length;
      notes = dryRun ? `dry-run: ${itemsProcessed} memories eligible for deep-sleep governance` : `assessed ${itemsProcessed} memories for deep-sleep governance`;
    }
  }
  const completedAt = (/* @__PURE__ */ new Date()).toISOString();
  const durationMs = Math.max(0, Date.parse(completedAt) - Date.parse(startedAt));
  let ledgerEntry;
  try {
    ledgerEntry = await recordDreamsPhaseRun({
      memoryDir,
      startedAt,
      completedAt,
      phase,
      itemsProcessed,
      dryRun,
      trigger: "manual",
      notes
    });
  } catch {
  }
  return {
    phase,
    dryRun,
    durationMs: ledgerEntry?.durationMs ?? durationMs,
    itemsProcessed,
    notes,
    ledgerEntry
  };
}
function summarizeGovernanceResultForDreams(govResult, dryRun) {
  const proposedCount = govResult.proposedActions.length;
  const appliedCount = govResult.appliedActions.length;
  const scannedCount = typeof govResult.summary?.scannedMemories === "number" && Number.isFinite(govResult.summary.scannedMemories) ? Math.max(0, Math.floor(govResult.summary.scannedMemories)) : govResult.reviewQueue.length;
  return {
    scannedMemories: scannedCount,
    appliedActionCount: appliedCount,
    notes: dryRun ? `shadow mode: ${proposedCount} actions proposed` : `applied ${appliedCount} actions`
  };
}
async function listMemoryFiles(memoryDir) {
  const out = [];
  try {
    const root = await lstat(memoryDir);
    if (root.isSymbolicLink()) {
      throw new Error(`memoryDir must not be a symlink: ${memoryDir}`);
    }
    if (!root.isDirectory()) return out;
  } catch (err) {
    if (err instanceof Error && /must not be a symlink/.test(err.message)) {
      throw err;
    }
    return out;
  }
  async function walk(dir) {
    let entries;
    try {
      entries = await readdir(dir, { withFileTypes: true });
    } catch {
      return;
    }
    for (const entry of entries) {
      if (entry.isSymbolicLink()) continue;
      const full = path.join(dir, entry.name);
      if (entry.isDirectory()) {
        if (entry.name === "state" || entry.name === "archive" || entry.name === "namespaces" || entry.name === ".git") continue;
        await walk(full);
      } else if (entry.isFile() && entry.name.endsWith(".md")) {
        out.push(full);
      }
    }
  }
  await walk(memoryDir);
  return out;
}
export {
  appendDreamsLedgerEntry,
  dreamsLedgerPath,
  getDreamsStatus,
  normalizeDreamsStatusWindowHours,
  readDreamsLedgerEntries,
  recordDreamsPhaseRun,
  runDreamsPhase,
  summarizeGovernanceResultForDreams
};
//# sourceMappingURL=dreams-ledger-LR2NBAZE.js.map