import "./chunk-PZ5AY32C.js";

// src/maintenance/purge.ts
import path from "path";
import { appendFile, mkdir, unlink } from "fs/promises";
function hasErrorCode(err, code) {
  return typeof err === "object" && err !== null && "code" in err && err.code === code;
}
function resolveTimestamp(memory) {
  const fm = memory.frontmatter;
  const updated = typeof fm.updated === "string" ? fm.updated : "";
  const created = typeof fm.created === "string" ? fm.created : "";
  return updated.length > 0 ? updated : created;
}
async function logPurgeAudit(storage, candidates, now, event = "PURGE_HARD_DELETE") {
  if (candidates.length === 0) return;
  const ledgerDir = path.join(storage.dir, "state", "observation-ledger");
  await mkdir(ledgerDir, { recursive: true });
  const ledgerPath = path.join(ledgerDir, "purge-audit.jsonl");
  const entries = candidates.map(
    (c) => JSON.stringify({
      event,
      timestamp: now.toISOString(),
      memoryId: c.id,
      path: c.path,
      tier: c.tier,
      status: c.status,
      updatedOrCreated: c.updatedOrCreated
    })
  );
  if (entries.length > 0) {
    await appendFile(ledgerPath, `${entries.join("\n")}
`, "utf-8");
  }
}
async function purgeMemories(options) {
  const {
    storage,
    olderThanMs,
    tier = "cold",
    forgottenOnly = false,
    dryRun = true,
    qmd,
    hotCollection = "openclaw-engram",
    coldCollection = "openclaw-engram-cold"
  } = options;
  if (!Number.isFinite(olderThanMs) || olderThanMs <= 0) {
    throw new Error("olderThanMs must be a finite positive number");
  }
  const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
  const nowMs = now.getTime();
  const [hotMemories, coldMemories, archivedMemories] = await Promise.all([
    tier === "all" ? storage.readAllMemories() : Promise.resolve([]),
    storage.readAllColdMemories(),
    tier === "all" ? storage.readArchivedMemories() : Promise.resolve([])
  ]);
  const poolEntries = [];
  if (tier === "all") {
    for (const m of hotMemories) {
      poolEntries.push({ memory: m, resolvedTier: "hot" });
    }
    for (const m of archivedMemories) {
      poolEntries.push({ memory: m, resolvedTier: "archive" });
    }
  }
  for (const m of coldMemories) {
    poolEntries.push({ memory: m, resolvedTier: "cold" });
  }
  const candidates = [];
  const candidateMemoriesById = /* @__PURE__ */ new Map();
  for (const { memory, resolvedTier } of poolEntries) {
    const ts = resolveTimestamp(memory);
    if (ts.length === 0) continue;
    const tsMs = Date.parse(ts);
    if (!Number.isFinite(tsMs)) continue;
    const ageMs = nowMs - tsMs;
    if (ageMs < olderThanMs) continue;
    const fm = memory.frontmatter;
    const status = typeof fm.status === "string" ? fm.status : "active";
    if (forgottenOnly && status !== "forgotten") continue;
    candidates.push({
      id: memory.frontmatter.id,
      path: memory.path,
      tier: resolvedTier,
      status,
      updatedOrCreated: ts,
      ageMs
    });
    candidateMemoriesById.set(memory.frontmatter.id, memory);
  }
  if (dryRun) {
    return {
      dryRun: true,
      tier,
      olderThanMs,
      candidates,
      purgedCount: 0,
      alreadyAbsentCount: 0,
      errorCount: 0,
      errors: []
    };
  }
  const errors = [];
  try {
    await logPurgeAudit(storage, candidates, now, "PURGE_DELETE_INTENT");
  } catch (auditErr) {
    errors.push({
      id: "(purge-audit)",
      path: path.join(storage.dir, "state", "observation-ledger", "purge-audit.jsonl"),
      error: auditErr instanceof Error ? auditErr.message : String(auditErr)
    });
  }
  const actuallyPurged = [];
  const alreadyAbsent = [];
  const collectionsToUpdate = /* @__PURE__ */ new Set();
  const addCollectionForCandidate = (candidate) => {
    if (candidate.tier === "cold") {
      collectionsToUpdate.add(coldCollection);
    } else if (candidate.tier === "hot") {
      collectionsToUpdate.add(hotCollection);
    }
  };
  for (const candidate of candidates) {
    try {
      await unlink(candidate.path);
      actuallyPurged.push(candidate);
      addCollectionForCandidate(candidate);
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      if (hasErrorCode(err, "ENOENT")) {
        alreadyAbsent.push(candidate);
        addCollectionForCandidate(candidate);
      } else {
        errors.push({ id: candidate.id, path: candidate.path, error: message });
      }
    }
  }
  const recordPostDeleteAudit = async (purgedCandidates, event) => {
    try {
      await logPurgeAudit(storage, purgedCandidates, now, event);
    } catch (auditErr) {
      errors.push({
        id: "(purge-audit)",
        path: path.join(storage.dir, "state", "observation-ledger", "purge-audit.jsonl"),
        error: auditErr instanceof Error ? auditErr.message : String(auditErr)
      });
    }
  };
  await recordPostDeleteAudit(actuallyPurged, "PURGE_HARD_DELETE");
  await recordPostDeleteAudit(alreadyAbsent, "PURGE_ALREADY_ABSENT");
  const resolvedPurges = [...actuallyPurged, ...alreadyAbsent];
  const invalidateTierCaches = storage.invalidateMemoryCachesForTiers;
  if (typeof invalidateTierCaches === "function") {
    invalidateTierCaches.call(storage, new Set(resolvedPurges.map((candidate) => candidate.tier)));
  } else if (typeof storage.invalidateAllMemoriesCacheForDir === "function") {
    storage.invalidateAllMemoriesCacheForDir();
  }
  if (qmd) {
    for (const collection of collectionsToUpdate) {
      try {
        if (typeof qmd.updateCollectionStrict === "function") {
          await qmd.updateCollectionStrict(collection, { signal: options.signal });
        } else {
          await qmd.updateCollection(collection, { signal: options.signal });
        }
      } catch (indexErr) {
        errors.push({
          id: "(qmd-update)",
          path: collection,
          error: indexErr instanceof Error ? indexErr.message : String(indexErr)
        });
      }
    }
  }
  const purgedFactMemories = resolvedPurges.map((candidate) => candidateMemoriesById.get(candidate.id)).filter((memory) => memory?.frontmatter.category === "fact");
  if (purgedFactMemories.length > 0) {
    const removeFactHashes = storage.removeFactContentHashesForMemories;
    if (typeof removeFactHashes === "function") {
      try {
        await removeFactHashes.call(storage, purgedFactMemories);
        await options.afterFactHashRemoval?.();
      } catch (hashErr) {
        errors.push({
          id: "(fact-hash-index)",
          path: path.join(storage.dir, "state", "fact-hashes.txt"),
          error: hashErr instanceof Error ? hashErr.message : String(hashErr)
        });
      }
    }
  }
  return {
    dryRun: false,
    tier,
    olderThanMs,
    candidates,
    purgedCount: actuallyPurged.length,
    alreadyAbsentCount: alreadyAbsent.length,
    errorCount: errors.length,
    errors
  };
}
export {
  purgeMemories
};
//# sourceMappingURL=purge-6ATBGT77.js.map