@joshuaswarren/openclaw-engram 8.3.86 → 8.3.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -456,9 +456,9 @@ function parseConfig(raw) {
456
456
  }
457
457
 
458
458
  // src/orchestrator.ts
459
- import path27 from "path";
459
+ import path28 from "path";
460
460
  import { createHash as createHash6 } from "crypto";
461
- import { mkdir as mkdir20, readdir as readdir11, readFile as readFile19, writeFile as writeFile18 } from "fs/promises";
461
+ import { mkdir as mkdir20, readdir as readdir12, readFile as readFile20, writeFile as writeFile19 } from "fs/promises";
462
462
 
463
463
  // src/signal.ts
464
464
  var BUILTIN_HIGH_PATTERNS = [
@@ -7505,8 +7505,410 @@ function extractTopics(memories, topN = 50) {
7505
7505
  }
7506
7506
 
7507
7507
  // src/transcript.ts
7508
- import { appendFile as appendFile2, mkdir as mkdir4, readdir as readdir3, readFile as readFile4, stat as stat2, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
7508
+ import { appendFile as appendFile2, mkdir as mkdir4, readdir as readdir4, readFile as readFile5, stat as stat2, unlink as unlink3, writeFile as writeFile5 } from "fs/promises";
7509
+ import path7 from "path";
7510
+
7511
+ // src/session-integrity.ts
7509
7512
  import path6 from "path";
7513
+ import { readFile as readFile4, readdir as readdir3, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
7514
+ function isObjectRecord(input) {
7515
+ return Boolean(input) && typeof input === "object";
7516
+ }
7517
+ function isTranscriptEntry(raw) {
7518
+ if (!isObjectRecord(raw)) return false;
7519
+ if (raw.role !== "user" && raw.role !== "assistant") return false;
7520
+ return typeof raw.timestamp === "string" && raw.timestamp.length > 0 && typeof raw.content === "string" && typeof raw.sessionKey === "string" && raw.sessionKey.length > 0 && typeof raw.turnId === "string" && raw.turnId.length > 0;
7521
+ }
7522
+ async function listTranscriptFiles(memoryDir) {
7523
+ const transcriptsDir = path6.join(memoryDir, "transcripts");
7524
+ const out = [];
7525
+ const stack = [transcriptsDir];
7526
+ while (stack.length > 0) {
7527
+ const dir = stack.pop();
7528
+ if (!dir) continue;
7529
+ let entries;
7530
+ try {
7531
+ entries = await readdir3(dir, { withFileTypes: true });
7532
+ } catch {
7533
+ continue;
7534
+ }
7535
+ for (const entry of entries) {
7536
+ const fullPath = path6.join(dir, entry.name);
7537
+ if (entry.isDirectory()) {
7538
+ stack.push(fullPath);
7539
+ } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
7540
+ out.push(fullPath);
7541
+ }
7542
+ }
7543
+ }
7544
+ return out.sort((a, b) => a.localeCompare(b));
7545
+ }
7546
+ async function parseTranscriptFile(filePath) {
7547
+ const bySession = /* @__PURE__ */ new Map();
7548
+ const malformed = [];
7549
+ const invalid = [];
7550
+ const invalidBySession = /* @__PURE__ */ new Map();
7551
+ let raw = "";
7552
+ try {
7553
+ raw = await readFile4(filePath, "utf-8");
7554
+ } catch {
7555
+ return { bySession, malformed, invalid, invalidBySession };
7556
+ }
7557
+ const lines = raw.split("\n");
7558
+ for (let index = 0; index < lines.length; index += 1) {
7559
+ const line = lines[index]?.trim() ?? "";
7560
+ if (!line) continue;
7561
+ let parsed;
7562
+ try {
7563
+ parsed = JSON.parse(line);
7564
+ } catch {
7565
+ malformed.push({
7566
+ code: "transcript_malformed_line",
7567
+ severity: "warn",
7568
+ message: "Transcript line is not valid JSON.",
7569
+ filePath,
7570
+ line: index + 1
7571
+ });
7572
+ continue;
7573
+ }
7574
+ if (!isTranscriptEntry(parsed)) {
7575
+ const sessionKey = isObjectRecord(parsed) && typeof parsed.sessionKey === "string" && parsed.sessionKey.length > 0 ? parsed.sessionKey : void 0;
7576
+ invalid.push({
7577
+ code: "transcript_invalid_entry",
7578
+ severity: "warn",
7579
+ message: "Transcript entry is missing required fields.",
7580
+ filePath,
7581
+ line: index + 1,
7582
+ sessionKey
7583
+ });
7584
+ if (sessionKey) {
7585
+ invalidBySession.set(sessionKey, (invalidBySession.get(sessionKey) ?? 0) + 1);
7586
+ }
7587
+ continue;
7588
+ }
7589
+ const list = bySession.get(parsed.sessionKey) ?? [];
7590
+ list.push({ filePath, lineNumber: index + 1, entry: parsed });
7591
+ bySession.set(parsed.sessionKey, list);
7592
+ }
7593
+ return { bySession, malformed, invalid, invalidBySession };
7594
+ }
7595
+ function analyzeSessionEntries(sessionKey, refs) {
7596
+ function parseTimestampForSort(timestamp) {
7597
+ const parsed = Date.parse(timestamp);
7598
+ if (Number.isFinite(parsed)) return parsed;
7599
+ return Number.MAX_SAFE_INTEGER;
7600
+ }
7601
+ const issues = [];
7602
+ const sorted = [...refs].sort((a, b) => {
7603
+ const tsA = parseTimestampForSort(a.entry.timestamp);
7604
+ const tsB = parseTimestampForSort(b.entry.timestamp);
7605
+ if (tsA !== tsB) return tsA - tsB;
7606
+ const rawTimestampCmp = a.entry.timestamp.localeCompare(b.entry.timestamp);
7607
+ if (rawTimestampCmp !== 0) return rawTimestampCmp;
7608
+ return a.entry.turnId.localeCompare(b.entry.turnId);
7609
+ });
7610
+ const turnIdSeen = /* @__PURE__ */ new Set();
7611
+ let duplicateTurnIds = 0;
7612
+ let brokenChains = 0;
7613
+ for (let i = 0; i < sorted.length; i += 1) {
7614
+ const current = sorted[i];
7615
+ if (turnIdSeen.has(current.entry.turnId)) {
7616
+ duplicateTurnIds += 1;
7617
+ issues.push({
7618
+ code: "transcript_duplicate_turn_id",
7619
+ severity: "warn",
7620
+ message: `Duplicate turnId detected: ${current.entry.turnId}`,
7621
+ sessionKey,
7622
+ filePath: current.filePath,
7623
+ line: current.lineNumber
7624
+ });
7625
+ } else {
7626
+ turnIdSeen.add(current.entry.turnId);
7627
+ }
7628
+ if (i > 0) {
7629
+ const previous = sorted[i - 1];
7630
+ if (previous && previous.entry.role === current.entry.role) {
7631
+ brokenChains += 1;
7632
+ issues.push({
7633
+ code: "transcript_broken_chain",
7634
+ severity: "warn",
7635
+ message: `Adjacent turns have the same role (${current.entry.role}).`,
7636
+ sessionKey,
7637
+ filePath: current.filePath,
7638
+ line: current.lineNumber
7639
+ });
7640
+ }
7641
+ }
7642
+ }
7643
+ let incompleteTurns = 0;
7644
+ if (sorted.length > 0 && sorted[sorted.length - 1]?.entry.role === "user") {
7645
+ incompleteTurns = 1;
7646
+ const last = sorted[sorted.length - 1];
7647
+ issues.push({
7648
+ code: "transcript_incomplete_turn",
7649
+ severity: "warn",
7650
+ message: "Session ends on a user turn without assistant response.",
7651
+ sessionKey,
7652
+ filePath: last?.filePath,
7653
+ line: last?.lineNumber
7654
+ });
7655
+ }
7656
+ return {
7657
+ stats: {
7658
+ sessionKey,
7659
+ entries: sorted.length,
7660
+ malformedLines: 0,
7661
+ invalidEntries: 0,
7662
+ duplicateTurnIds,
7663
+ brokenChains,
7664
+ incompleteTurns
7665
+ },
7666
+ issues
7667
+ };
7668
+ }
7669
+ function validateCheckpointRaw(checkpoint) {
7670
+ if (!isObjectRecord(checkpoint)) return false;
7671
+ return typeof checkpoint.sessionKey === "string" && checkpoint.sessionKey.length > 0 && typeof checkpoint.capturedAt === "string" && typeof checkpoint.ttl === "string" && Array.isArray(checkpoint.turns);
7672
+ }
7673
+ async function analyzeCheckpoint(memoryDir) {
7674
+ const checkpointPath = path6.join(memoryDir, "state", "checkpoint.json");
7675
+ const issues = [];
7676
+ const checkpoint = {
7677
+ present: false,
7678
+ healthy: true,
7679
+ path: checkpointPath
7680
+ };
7681
+ let raw = "";
7682
+ try {
7683
+ raw = await readFile4(checkpointPath, "utf-8");
7684
+ } catch {
7685
+ issues.push({
7686
+ code: "checkpoint_missing",
7687
+ severity: "info",
7688
+ message: "No checkpoint file present.",
7689
+ filePath: checkpointPath
7690
+ });
7691
+ return { checkpoint, issues };
7692
+ }
7693
+ checkpoint.present = true;
7694
+ let parsed;
7695
+ try {
7696
+ parsed = JSON.parse(raw);
7697
+ } catch {
7698
+ checkpoint.healthy = false;
7699
+ issues.push({
7700
+ code: "checkpoint_invalid_json",
7701
+ severity: "error",
7702
+ message: "Checkpoint file is invalid JSON.",
7703
+ filePath: checkpointPath
7704
+ });
7705
+ return { checkpoint, issues };
7706
+ }
7707
+ if (!validateCheckpointRaw(parsed)) {
7708
+ checkpoint.healthy = false;
7709
+ issues.push({
7710
+ code: "checkpoint_invalid_metadata",
7711
+ severity: "error",
7712
+ message: "Checkpoint file is missing required metadata fields.",
7713
+ filePath: checkpointPath
7714
+ });
7715
+ return { checkpoint, issues };
7716
+ }
7717
+ checkpoint.sessionKey = parsed.sessionKey;
7718
+ checkpoint.expiresAt = parsed.ttl;
7719
+ const ttlMs = Date.parse(parsed.ttl);
7720
+ const capturedAtMs = Date.parse(parsed.capturedAt);
7721
+ if (!Number.isFinite(ttlMs) || !Number.isFinite(capturedAtMs) || ttlMs <= capturedAtMs) {
7722
+ checkpoint.healthy = false;
7723
+ issues.push({
7724
+ code: "checkpoint_invalid_metadata",
7725
+ severity: "error",
7726
+ message: "Checkpoint timestamps are invalid or inconsistent.",
7727
+ filePath: checkpointPath,
7728
+ sessionKey: parsed.sessionKey
7729
+ });
7730
+ return { checkpoint, issues };
7731
+ }
7732
+ if (ttlMs < Date.now()) {
7733
+ checkpoint.healthy = false;
7734
+ issues.push({
7735
+ code: "checkpoint_expired",
7736
+ severity: "warn",
7737
+ message: "Checkpoint TTL has expired.",
7738
+ filePath: checkpointPath,
7739
+ sessionKey: parsed.sessionKey
7740
+ });
7741
+ }
7742
+ for (const turn of parsed.turns) {
7743
+ if (!isTranscriptEntry(turn)) {
7744
+ checkpoint.healthy = false;
7745
+ issues.push({
7746
+ code: "checkpoint_invalid_metadata",
7747
+ severity: "error",
7748
+ message: "Checkpoint contains invalid turn entries.",
7749
+ filePath: checkpointPath,
7750
+ sessionKey: parsed.sessionKey
7751
+ });
7752
+ break;
7753
+ }
7754
+ }
7755
+ return { checkpoint, issues };
7756
+ }
7757
+ async function analyzeSessionIntegrity(options) {
7758
+ const memoryDir = options.memoryDir;
7759
+ const reportIssues = [];
7760
+ const allSessionRefs = /* @__PURE__ */ new Map();
7761
+ const invalidBySession = /* @__PURE__ */ new Map();
7762
+ const sessions = /* @__PURE__ */ new Map();
7763
+ const files = await listTranscriptFiles(memoryDir);
7764
+ for (const filePath of files) {
7765
+ const parsed = await parseTranscriptFile(filePath);
7766
+ reportIssues.push(...parsed.malformed, ...parsed.invalid);
7767
+ for (const [sessionKey, count] of parsed.invalidBySession.entries()) {
7768
+ invalidBySession.set(sessionKey, (invalidBySession.get(sessionKey) ?? 0) + count);
7769
+ }
7770
+ for (const [sessionKey, refs] of parsed.bySession.entries()) {
7771
+ const existing = allSessionRefs.get(sessionKey) ?? [];
7772
+ existing.push(...refs);
7773
+ allSessionRefs.set(sessionKey, existing);
7774
+ }
7775
+ }
7776
+ for (const [sessionKey, refs] of allSessionRefs.entries()) {
7777
+ const analyzed = analyzeSessionEntries(sessionKey, refs);
7778
+ reportIssues.push(...analyzed.issues);
7779
+ sessions.set(sessionKey, {
7780
+ ...analyzed.stats,
7781
+ malformedLines: 0,
7782
+ invalidEntries: invalidBySession.get(sessionKey) ?? 0
7783
+ });
7784
+ }
7785
+ const checkpoint = await analyzeCheckpoint(memoryDir);
7786
+ reportIssues.push(...checkpoint.issues);
7787
+ const severeIssueCount = reportIssues.filter((issue) => issue.severity !== "info").length;
7788
+ return {
7789
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7790
+ memoryDir,
7791
+ healthy: severeIssueCount === 0,
7792
+ sessions: [...sessions.values()].sort((a, b) => a.sessionKey.localeCompare(b.sessionKey)),
7793
+ checkpoint: checkpoint.checkpoint,
7794
+ issues: reportIssues
7795
+ };
7796
+ }
7797
+ function collectTranscriptRewriteTargets(report) {
7798
+ const set = /* @__PURE__ */ new Set();
7799
+ for (const issue of report.issues) {
7800
+ if (!issue.filePath) continue;
7801
+ if (issue.code === "transcript_malformed_line" || issue.code === "transcript_invalid_entry") {
7802
+ set.add(issue.filePath);
7803
+ }
7804
+ }
7805
+ return [...set].sort((a, b) => a.localeCompare(b));
7806
+ }
7807
+ function planSessionRepair(options) {
7808
+ const actions = [];
7809
+ const transcriptTargets = collectTranscriptRewriteTargets(options.report);
7810
+ for (const targetPath of transcriptTargets) {
7811
+ actions.push({
7812
+ kind: "rewrite_transcript",
7813
+ targetPath,
7814
+ description: "Rewrite transcript file with only valid JSON transcript entries."
7815
+ });
7816
+ }
7817
+ const checkpointNeedsRepair = options.report.issues.some(
7818
+ (issue) => issue.code === "checkpoint_invalid_json" || issue.code === "checkpoint_invalid_metadata" || issue.code === "checkpoint_expired"
7819
+ );
7820
+ if (checkpointNeedsRepair && options.report.checkpoint.present) {
7821
+ actions.push({
7822
+ kind: "remove_checkpoint",
7823
+ targetPath: options.report.checkpoint.path,
7824
+ description: "Remove invalid or expired checkpoint file."
7825
+ });
7826
+ }
7827
+ if (options.sessionFilesDir && options.allowSessionFileRepair === true) {
7828
+ actions.push({
7829
+ kind: "repair_session_files",
7830
+ targetPath: options.sessionFilesDir,
7831
+ description: "Session file repair was requested; no automatic rewiring is performed.",
7832
+ details: "No-op by design. OpenClaw session files require explicit manual review."
7833
+ });
7834
+ }
7835
+ return {
7836
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7837
+ dryRun: options.dryRun,
7838
+ allowSessionFileRepair: options.allowSessionFileRepair === true,
7839
+ actions
7840
+ };
7841
+ }
7842
+ async function rewriteTranscriptFile(targetPath) {
7843
+ let raw = "";
7844
+ try {
7845
+ raw = await readFile4(targetPath, "utf-8");
7846
+ } catch {
7847
+ return;
7848
+ }
7849
+ const lines = raw.split("\n");
7850
+ const validLines = [];
7851
+ for (const line of lines) {
7852
+ const trimmed = line.trim();
7853
+ if (!trimmed) continue;
7854
+ try {
7855
+ const parsed = JSON.parse(trimmed);
7856
+ if (!isTranscriptEntry(parsed)) continue;
7857
+ validLines.push(JSON.stringify(parsed));
7858
+ } catch {
7859
+ }
7860
+ }
7861
+ const body = validLines.length > 0 ? `${validLines.join("\n")}
7862
+ ` : "";
7863
+ await writeFile4(targetPath, body, "utf-8");
7864
+ }
7865
+ async function applySessionRepair(options) {
7866
+ const { plan } = options;
7867
+ if (plan.dryRun) {
7868
+ return {
7869
+ applied: false,
7870
+ actionsAttempted: plan.actions.length,
7871
+ actionsApplied: 0,
7872
+ errors: []
7873
+ };
7874
+ }
7875
+ let actionsApplied = 0;
7876
+ const errors = [];
7877
+ for (const action of plan.actions) {
7878
+ try {
7879
+ if (action.kind === "rewrite_transcript") {
7880
+ await rewriteTranscriptFile(action.targetPath);
7881
+ actionsApplied += 1;
7882
+ continue;
7883
+ }
7884
+ if (action.kind === "remove_checkpoint") {
7885
+ try {
7886
+ await unlink2(action.targetPath);
7887
+ } catch (err) {
7888
+ const code = typeof err === "object" && err && "code" in err ? String(err.code ?? "") : "";
7889
+ if (code !== "ENOENT") {
7890
+ throw err;
7891
+ }
7892
+ }
7893
+ actionsApplied += 1;
7894
+ continue;
7895
+ }
7896
+ if (action.kind === "repair_session_files") {
7897
+ actionsApplied += 1;
7898
+ }
7899
+ } catch (err) {
7900
+ errors.push(`Failed ${action.kind} ${action.targetPath}: ${err instanceof Error ? err.message : String(err)}`);
7901
+ }
7902
+ }
7903
+ return {
7904
+ applied: true,
7905
+ actionsAttempted: plan.actions.length,
7906
+ actionsApplied,
7907
+ errors
7908
+ };
7909
+ }
7910
+
7911
+ // src/transcript.ts
7510
7912
  var TranscriptManager = class _TranscriptManager {
7511
7913
  transcriptsDir;
7512
7914
  checkpointPath;
@@ -7520,10 +7922,10 @@ var TranscriptManager = class _TranscriptManager {
7520
7922
  static CHARS_PER_TOKEN = 4;
7521
7923
  constructor(config) {
7522
7924
  this.config = config;
7523
- this.transcriptsDir = path6.join(config.memoryDir, "transcripts");
7524
- this.stateDir = path6.join(config.memoryDir, "state");
7525
- this.checkpointPath = path6.join(this.stateDir, "checkpoint.json");
7526
- this.toolUsageDir = path6.join(this.stateDir, "tool-usage");
7925
+ this.transcriptsDir = path7.join(config.memoryDir, "transcripts");
7926
+ this.stateDir = path7.join(config.memoryDir, "state");
7927
+ this.checkpointPath = path7.join(this.stateDir, "checkpoint.json");
7928
+ this.toolUsageDir = path7.join(this.stateDir, "tool-usage");
7527
7929
  }
7528
7930
  /**
7529
7931
  * Parse a sessionKey to extract channel type and ID.
@@ -7556,7 +7958,7 @@ var TranscriptManager = class _TranscriptManager {
7556
7958
  }
7557
7959
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7558
7960
  return {
7559
- dir: path6.join(channelType, channelId),
7961
+ dir: path7.join(channelType, channelId),
7560
7962
  file: `${today}.jsonl`
7561
7963
  };
7562
7964
  }
@@ -7578,19 +7980,19 @@ var TranscriptManager = class _TranscriptManager {
7578
7980
  const transcriptDir = this.transcriptsDir;
7579
7981
  const sessionKeys = /* @__PURE__ */ new Set();
7580
7982
  try {
7581
- const typeEntries = await readdir3(transcriptDir, { withFileTypes: true });
7983
+ const typeEntries = await readdir4(transcriptDir, { withFileTypes: true });
7582
7984
  for (const typeEnt of typeEntries) {
7583
7985
  if (!typeEnt.isDirectory()) continue;
7584
- const typeDir = path6.join(transcriptDir, typeEnt.name);
7585
- const idEntries = await readdir3(typeDir, { withFileTypes: true });
7986
+ const typeDir = path7.join(transcriptDir, typeEnt.name);
7987
+ const idEntries = await readdir4(typeDir, { withFileTypes: true });
7586
7988
  for (const idEnt of idEntries) {
7587
7989
  if (!idEnt.isDirectory()) continue;
7588
- const chanDir = path6.join(typeDir, idEnt.name);
7589
- const files = (await readdir3(chanDir)).filter((f) => f.endsWith(".jsonl")).sort();
7990
+ const chanDir = path7.join(typeDir, idEnt.name);
7991
+ const files = (await readdir4(chanDir)).filter((f) => f.endsWith(".jsonl")).sort();
7590
7992
  const last = files[files.length - 1];
7591
7993
  if (!last) continue;
7592
7994
  try {
7593
- const raw = await readFile4(path6.join(chanDir, last), "utf-8");
7995
+ const raw = await readFile5(path7.join(chanDir, last), "utf-8");
7594
7996
  const firstLine = raw.split("\n").find((l) => l.trim().length > 0);
7595
7997
  if (!firstLine) continue;
7596
7998
  const entry = JSON.parse(firstLine);
@@ -7612,21 +8014,21 @@ var TranscriptManager = class _TranscriptManager {
7612
8014
  }
7613
8015
  async appendToolUse(entry) {
7614
8016
  const { dir, file } = this.getToolUsagePath(entry.sessionKey);
7615
- const channelDir = path6.join(this.toolUsageDir, dir);
8017
+ const channelDir = path7.join(this.toolUsageDir, dir);
7616
8018
  await mkdir4(channelDir, { recursive: true });
7617
- const filePath = path6.join(channelDir, file);
8019
+ const filePath = path7.join(channelDir, file);
7618
8020
  await appendFile2(filePath, JSON.stringify(entry) + "\n", "utf-8");
7619
8021
  }
7620
8022
  async readToolUse(sessionKey, startTime, endTime) {
7621
8023
  const { dir } = this.getToolUsagePath(sessionKey);
7622
- const channelDir = path6.join(this.toolUsageDir, dir);
8024
+ const channelDir = path7.join(this.toolUsageDir, dir);
7623
8025
  try {
7624
- const files = await readdir3(channelDir);
8026
+ const files = await readdir4(channelDir);
7625
8027
  const out = [];
7626
8028
  for (const file of files) {
7627
8029
  if (!file.endsWith(".jsonl")) continue;
7628
- const fp = path6.join(channelDir, file);
7629
- const raw = await readFile4(fp, "utf-8");
8030
+ const fp = path7.join(channelDir, file);
8031
+ const raw = await readFile5(fp, "utf-8");
7630
8032
  for (const line of raw.split("\n")) {
7631
8033
  if (!line.trim()) continue;
7632
8034
  try {
@@ -7649,17 +8051,17 @@ var TranscriptManager = class _TranscriptManager {
7649
8051
  }
7650
8052
  async estimateSessionFootprint(sessionKey) {
7651
8053
  const { dir } = this.getTranscriptPath(sessionKey);
7652
- const channelDir = path6.join(this.transcriptsDir, dir);
8054
+ const channelDir = path7.join(this.transcriptsDir, dir);
7653
8055
  let bytes = 0;
7654
8056
  try {
7655
- const files = (await readdir3(channelDir)).filter((file) => file.endsWith(".jsonl")).sort();
8057
+ const files = (await readdir4(channelDir)).filter((file) => file.endsWith(".jsonl")).sort();
7656
8058
  const cached = this.sessionFootprintCache.get(sessionKey);
7657
8059
  if (!cached) {
7658
8060
  const fileBytes = /* @__PURE__ */ new Map();
7659
8061
  const fileSizes = /* @__PURE__ */ new Map();
7660
8062
  for (const file of files) {
7661
8063
  try {
7662
- const fullPath = path6.join(channelDir, file);
8064
+ const fullPath = path7.join(channelDir, file);
7663
8065
  const fileInfo = await stat2(fullPath);
7664
8066
  const sessionBytes = await this.estimateSessionBytesInFile(
7665
8067
  fullPath,
@@ -7685,7 +8087,7 @@ var TranscriptManager = class _TranscriptManager {
7685
8087
  for (const file of files) {
7686
8088
  if (cached.fileBytes.has(file)) continue;
7687
8089
  try {
7688
- const fullPath = path6.join(channelDir, file);
8090
+ const fullPath = path7.join(channelDir, file);
7689
8091
  const fileInfo = await stat2(fullPath);
7690
8092
  const sessionBytes = await this.estimateSessionBytesInFile(fullPath, sessionKey);
7691
8093
  cached.fileBytes.set(file, sessionBytes);
@@ -7697,7 +8099,7 @@ var TranscriptManager = class _TranscriptManager {
7697
8099
  const newestFile = files[files.length - 1];
7698
8100
  if (newestFile) {
7699
8101
  try {
7700
- const newestPath = path6.join(channelDir, newestFile);
8102
+ const newestPath = path7.join(channelDir, newestFile);
7701
8103
  const fileInfo = await stat2(newestPath);
7702
8104
  const size = Math.max(0, fileInfo.size);
7703
8105
  const previousSessionBytes = cached.fileBytes.get(newestFile) ?? 0;
@@ -7724,7 +8126,7 @@ var TranscriptManager = class _TranscriptManager {
7724
8126
  }
7725
8127
  async estimateSessionBytesInFile(filePath, sessionKey) {
7726
8128
  try {
7727
- const raw = await readFile4(filePath, "utf-8");
8129
+ const raw = await readFile5(filePath, "utf-8");
7728
8130
  let total = 0;
7729
8131
  for (const line of raw.split("\n")) {
7730
8132
  if (!line.trim()) continue;
@@ -7757,12 +8159,12 @@ var TranscriptManager = class _TranscriptManager {
7757
8159
  async append(entry) {
7758
8160
  try {
7759
8161
  const { dir, file } = this.getTranscriptPath(entry.sessionKey);
7760
- const channelType = dir.split(path6.sep)[0] ?? dir;
8162
+ const channelType = dir.split(path7.sep)[0] ?? dir;
7761
8163
  if (this.config.transcriptSkipChannelTypes.includes(channelType)) {
7762
8164
  return;
7763
8165
  }
7764
- const channelDir = path6.join(this.transcriptsDir, dir);
7765
- const filePath = path6.join(channelDir, file);
8166
+ const channelDir = path7.join(this.transcriptsDir, dir);
8167
+ const filePath = path7.join(channelDir, file);
7766
8168
  await mkdir4(channelDir, { recursive: true });
7767
8169
  const line = JSON.stringify(entry) + "\n";
7768
8170
  await appendFile2(filePath, line, "utf-8");
@@ -7779,26 +8181,26 @@ var TranscriptManager = class _TranscriptManager {
7779
8181
  async getAllTranscriptFiles() {
7780
8182
  const files = [];
7781
8183
  try {
7782
- const entries = await readdir3(this.transcriptsDir, { withFileTypes: true });
8184
+ const entries = await readdir4(this.transcriptsDir, { withFileTypes: true });
7783
8185
  for (const entry of entries) {
7784
8186
  if (entry.isDirectory()) {
7785
- const channelTypeDir = path6.join(this.transcriptsDir, entry.name);
8187
+ const channelTypeDir = path7.join(this.transcriptsDir, entry.name);
7786
8188
  try {
7787
- const channelTypeEntries = await readdir3(channelTypeDir, { withFileTypes: true });
8189
+ const channelTypeEntries = await readdir4(channelTypeDir, { withFileTypes: true });
7788
8190
  for (const channelTypeEntry of channelTypeEntries) {
7789
8191
  if (channelTypeEntry.isDirectory()) {
7790
- const channelDir = path6.join(channelTypeDir, channelTypeEntry.name);
8192
+ const channelDir = path7.join(channelTypeDir, channelTypeEntry.name);
7791
8193
  try {
7792
- const channelFiles = await readdir3(channelDir);
8194
+ const channelFiles = await readdir4(channelDir);
7793
8195
  for (const file of channelFiles) {
7794
8196
  if (file.endsWith(".jsonl")) {
7795
- files.push(path6.join(entry.name, channelTypeEntry.name, file));
8197
+ files.push(path7.join(entry.name, channelTypeEntry.name, file));
7796
8198
  }
7797
8199
  }
7798
8200
  } catch {
7799
8201
  }
7800
8202
  } else if (channelTypeEntry.isFile() && channelTypeEntry.name.endsWith(".jsonl")) {
7801
- files.push(path6.join(entry.name, channelTypeEntry.name));
8203
+ files.push(path7.join(entry.name, channelTypeEntry.name));
7802
8204
  }
7803
8205
  }
7804
8206
  } catch {
@@ -7823,9 +8225,9 @@ var TranscriptManager = class _TranscriptManager {
7823
8225
  try {
7824
8226
  const transcriptFiles = await this.getAllTranscriptFiles();
7825
8227
  for (const relativePath of transcriptFiles) {
7826
- const filePath = path6.join(this.transcriptsDir, relativePath);
8228
+ const filePath = path7.join(this.transcriptsDir, relativePath);
7827
8229
  try {
7828
- const content = await readFile4(filePath, "utf-8");
8230
+ const content = await readFile5(filePath, "utf-8");
7829
8231
  const lines = content.trim().split("\n").filter(Boolean);
7830
8232
  for (const line of lines) {
7831
8233
  try {
@@ -7875,26 +8277,26 @@ var TranscriptManager = class _TranscriptManager {
7875
8277
  cutoff.setHours(0, 0, 0, 0);
7876
8278
  let processed = 0;
7877
8279
  try {
7878
- const entries = await readdir3(this.transcriptsDir, { withFileTypes: true });
8280
+ const entries = await readdir4(this.transcriptsDir, { withFileTypes: true });
7879
8281
  for (const entry of entries) {
7880
8282
  if (entry.isDirectory()) {
7881
- const channelTypeDir = path6.join(this.transcriptsDir, entry.name);
8283
+ const channelTypeDir = path7.join(this.transcriptsDir, entry.name);
7882
8284
  try {
7883
- const channelTypeEntries = await readdir3(channelTypeDir, { withFileTypes: true });
8285
+ const channelTypeEntries = await readdir4(channelTypeDir, { withFileTypes: true });
7884
8286
  for (const channelTypeEntry of channelTypeEntries) {
7885
8287
  if (channelTypeEntry.isDirectory()) {
7886
- const channelDir = path6.join(channelTypeDir, channelTypeEntry.name);
8288
+ const channelDir = path7.join(channelTypeDir, channelTypeEntry.name);
7887
8289
  try {
7888
- const channelFiles = await readdir3(channelDir);
8290
+ const channelFiles = await readdir4(channelDir);
7889
8291
  for (const file of channelFiles) {
7890
8292
  if (!file.endsWith(".jsonl")) continue;
7891
- const filePath = path6.join(channelDir, file);
8293
+ const filePath = path7.join(channelDir, file);
7892
8294
  if (this.isLegacyTranscriptFile(file)) {
7893
8295
  const dateStr = file.slice(0, 10);
7894
8296
  const fileDate = new Date(dateStr);
7895
8297
  if (!isNaN(fileDate.getTime()) && fileDate < cutoff) {
7896
8298
  try {
7897
- await unlink2(filePath);
8299
+ await unlink3(filePath);
7898
8300
  processed++;
7899
8301
  log.debug(`deleted old daily transcript file: ${entry.name}/${channelTypeEntry.name}/${file}`);
7900
8302
  } catch (err) {
@@ -7912,7 +8314,7 @@ var TranscriptManager = class _TranscriptManager {
7912
8314
  log.debug(`failed to process channel directory ${entry.name}/${channelTypeEntry.name}:`, err);
7913
8315
  }
7914
8316
  } else if (channelTypeEntry.isFile() && channelTypeEntry.name.endsWith(".jsonl")) {
7915
- const filePath = path6.join(channelTypeDir, channelTypeEntry.name);
8317
+ const filePath = path7.join(channelTypeDir, channelTypeEntry.name);
7916
8318
  const cleaned = await this.cleanupTranscriptFile(filePath, cutoff);
7917
8319
  if (cleaned) {
7918
8320
  processed++;
@@ -7927,9 +8329,9 @@ var TranscriptManager = class _TranscriptManager {
7927
8329
  const dateStr = entry.name.slice(0, 10);
7928
8330
  const fileDate = new Date(dateStr);
7929
8331
  if (!isNaN(fileDate.getTime()) && fileDate < cutoff) {
7930
- const filePath = path6.join(this.transcriptsDir, entry.name);
8332
+ const filePath = path7.join(this.transcriptsDir, entry.name);
7931
8333
  try {
7932
- await unlink2(filePath);
8334
+ await unlink3(filePath);
7933
8335
  processed++;
7934
8336
  log.debug(`deleted old legacy transcript file: ${entry.name}`);
7935
8337
  } catch (err) {
@@ -7955,7 +8357,7 @@ var TranscriptManager = class _TranscriptManager {
7955
8357
  */
7956
8358
  async cleanupTranscriptFile(filePath, cutoff) {
7957
8359
  try {
7958
- const content = await readFile4(filePath, "utf-8");
8360
+ const content = await readFile5(filePath, "utf-8");
7959
8361
  const lines = content.trim().split("\n").filter(Boolean);
7960
8362
  const validLines = [];
7961
8363
  let hasOldEntries = false;
@@ -7974,7 +8376,7 @@ var TranscriptManager = class _TranscriptManager {
7974
8376
  }
7975
8377
  if (validLines.length === 0) {
7976
8378
  try {
7977
- await unlink2(filePath);
8379
+ await unlink3(filePath);
7978
8380
  log.debug(`deleted empty transcript file: ${filePath}`);
7979
8381
  return true;
7980
8382
  } catch (err) {
@@ -7983,7 +8385,7 @@ var TranscriptManager = class _TranscriptManager {
7983
8385
  }
7984
8386
  }
7985
8387
  if (hasOldEntries) {
7986
- await writeFile4(filePath, validLines.join("\n") + "\n", "utf-8");
8388
+ await writeFile5(filePath, validLines.join("\n") + "\n", "utf-8");
7987
8389
  log.debug(`cleaned old entries from transcript file: ${filePath}`);
7988
8390
  return true;
7989
8391
  }
@@ -7998,7 +8400,7 @@ var TranscriptManager = class _TranscriptManager {
7998
8400
  */
7999
8401
  async saveCheckpoint(checkpoint) {
8000
8402
  try {
8001
- await writeFile4(this.checkpointPath, JSON.stringify(checkpoint, null, 2), "utf-8");
8403
+ await writeFile5(this.checkpointPath, JSON.stringify(checkpoint, null, 2), "utf-8");
8002
8404
  log.info(`saved checkpoint for session ${checkpoint.sessionKey} with ${checkpoint.turns.length} turn(s)`);
8003
8405
  } catch (err) {
8004
8406
  log.error("failed to save checkpoint:", err);
@@ -8011,7 +8413,7 @@ var TranscriptManager = class _TranscriptManager {
8011
8413
  */
8012
8414
  async loadCheckpoint(sessionKey) {
8013
8415
  try {
8014
- const raw = await readFile4(this.checkpointPath, "utf-8");
8416
+ const raw = await readFile5(this.checkpointPath, "utf-8");
8015
8417
  const checkpoint = JSON.parse(raw);
8016
8418
  if (!checkpoint.sessionKey || !checkpoint.capturedAt || !checkpoint.ttl || !Array.isArray(checkpoint.turns)) {
8017
8419
  log.warn("checkpoint file has invalid structure");
@@ -8043,7 +8445,7 @@ var TranscriptManager = class _TranscriptManager {
8043
8445
  */
8044
8446
  async clearCheckpoint() {
8045
8447
  try {
8046
- await unlink2(this.checkpointPath);
8448
+ await unlink3(this.checkpointPath);
8047
8449
  log.info("cleared checkpoint");
8048
8450
  } catch (err) {
8049
8451
  log.debug("no checkpoint to clear");
@@ -8139,12 +8541,12 @@ var TranscriptManager = class _TranscriptManager {
8139
8541
  let totalEntries = 0;
8140
8542
  const channelTypes = {};
8141
8543
  for (const relativePath of allFiles) {
8142
- const filePath = path6.join(this.transcriptsDir, relativePath);
8544
+ const filePath = path7.join(this.transcriptsDir, relativePath);
8143
8545
  try {
8144
- const content = await readFile4(filePath, "utf-8");
8546
+ const content = await readFile5(filePath, "utf-8");
8145
8547
  const lines = content.trim().split("\n").filter(Boolean);
8146
8548
  totalEntries += lines.length;
8147
- const channelType = relativePath.includes(path6.sep) ? relativePath.split(path6.sep)[0] : "legacy";
8549
+ const channelType = relativePath.includes(path7.sep) ? relativePath.split(path7.sep)[0] : "legacy";
8148
8550
  channelTypes[channelType] = (channelTypes[channelType] || 0) + 1;
8149
8551
  } catch {
8150
8552
  }
@@ -8167,11 +8569,32 @@ var TranscriptManager = class _TranscriptManager {
8167
8569
  };
8168
8570
  }
8169
8571
  }
8572
+ async analyzeIntegrity() {
8573
+ return analyzeSessionIntegrity({ memoryDir: this.config.memoryDir });
8574
+ }
8575
+ async getRecoverySummary(sessionKey) {
8576
+ const report = await this.analyzeIntegrity();
8577
+ const selectedSessions = sessionKey ? report.sessions.filter((session) => session.sessionKey === sessionKey) : report.sessions;
8578
+ const incompleteTurns = selectedSessions.reduce((sum, session) => sum + session.incompleteTurns, 0);
8579
+ const brokenChains = selectedSessions.reduce((sum, session) => sum + session.brokenChains, 0);
8580
+ const filteredIssues = report.issues.filter((issue) => !sessionKey || issue.sessionKey === sessionKey);
8581
+ const issueCount = filteredIssues.length;
8582
+ const severeIssueCount = filteredIssues.filter((issue) => issue.severity !== "info").length;
8583
+ return {
8584
+ generatedAt: report.generatedAt,
8585
+ sessionKey,
8586
+ healthy: sessionKey ? severeIssueCount === 0 && report.checkpoint.healthy : report.healthy,
8587
+ issueCount,
8588
+ incompleteTurns,
8589
+ brokenChains,
8590
+ checkpointHealthy: report.checkpoint.healthy
8591
+ };
8592
+ }
8170
8593
  };
8171
8594
 
8172
8595
  // src/summarizer.ts
8173
- import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5, readdir as readdir4 } from "fs/promises";
8174
- import path7 from "path";
8596
+ import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile6, readdir as readdir5 } from "fs/promises";
8597
+ import path8 from "path";
8175
8598
  import { z as z2 } from "zod";
8176
8599
  var HourlySummarySchema = z2.object({
8177
8600
  bullets: z2.array(z2.string()).describe("3-5 bullet points summarizing the hour's activity")
@@ -8191,7 +8614,7 @@ var HourlySummarizer = class {
8191
8614
  transcript;
8192
8615
  constructor(config, gatewayConfig, modelRegistry, transcript) {
8193
8616
  this.config = config;
8194
- this.summariesDir = path7.join(config.memoryDir, "summaries", "hourly");
8617
+ this.summariesDir = path8.join(config.memoryDir, "summaries", "hourly");
8195
8618
  this.modelRegistry = modelRegistry ?? new ModelRegistry(config.memoryDir);
8196
8619
  this.transcript = transcript;
8197
8620
  this.localLlm = new LocalLlmClient(config, this.modelRegistry);
@@ -8459,15 +8882,15 @@ ${truncatedConversation}`;
8459
8882
  }
8460
8883
  // Save summary to file
8461
8884
  async saveSummary(summary) {
8462
- const sessionDir = path7.join(this.summariesDir, summary.sessionKey);
8885
+ const sessionDir = path8.join(this.summariesDir, summary.sessionKey);
8463
8886
  await mkdir5(sessionDir, { recursive: true });
8464
8887
  const dateStr = summary.hour.slice(0, 10);
8465
- const filePath = path7.join(sessionDir, `${dateStr}.md`);
8888
+ const filePath = path8.join(sessionDir, `${dateStr}.md`);
8466
8889
  const hourStr = summary.hour.slice(11, 13);
8467
8890
  const lines = [];
8468
8891
  let existingContent = "";
8469
8892
  try {
8470
- existingContent = await readFile5(filePath, "utf-8");
8893
+ existingContent = await readFile6(filePath, "utf-8");
8471
8894
  } catch {
8472
8895
  }
8473
8896
  const hourHeader = `## ${hourStr}:00`;
@@ -8481,18 +8904,18 @@ ${truncatedConversation}`;
8481
8904
  } else {
8482
8905
  existingContent = beforeHour + newSection;
8483
8906
  }
8484
- await writeFile5(filePath, existingContent, "utf-8");
8907
+ await writeFile6(filePath, existingContent, "utf-8");
8485
8908
  log.debug(`updated hourly summary for ${summary.sessionKey} at ${hourStr}:00`);
8486
8909
  } else {
8487
8910
  const newSection = this.formatHourSection(summary, hourHeader);
8488
8911
  if (existingContent) {
8489
- await writeFile5(filePath, existingContent.trimEnd() + "\n\n" + newSection, "utf-8");
8912
+ await writeFile6(filePath, existingContent.trimEnd() + "\n\n" + newSection, "utf-8");
8490
8913
  } else {
8491
8914
  const header = `# Hourly Summaries \u2014 ${dateStr}
8492
8915
 
8493
8916
  *Session: ${summary.sessionKey}*
8494
8917
  `;
8495
- await writeFile5(filePath, header + "\n" + newSection, "utf-8");
8918
+ await writeFile6(filePath, header + "\n" + newSection, "utf-8");
8496
8919
  }
8497
8920
  log.debug(`saved hourly summary for ${summary.sessionKey} at ${hourStr}:00`);
8498
8921
  }
@@ -8537,15 +8960,15 @@ ${truncatedConversation}`;
8537
8960
  }
8538
8961
  // Read recent summaries for recall injection
8539
8962
  async readRecent(sessionKey, hours) {
8540
- const sessionDir = path7.join(this.summariesDir, sessionKey);
8963
+ const sessionDir = path8.join(this.summariesDir, sessionKey);
8541
8964
  try {
8542
- const files = await readdir4(sessionDir);
8965
+ const files = await readdir5(sessionDir);
8543
8966
  const mdFiles = files.filter((f) => f.endsWith(".md"));
8544
8967
  const summaries = [];
8545
8968
  const cutoffTime = Date.now() - hours * 60 * 60 * 1e3;
8546
8969
  for (const file of mdFiles) {
8547
- const filePath = path7.join(sessionDir, file);
8548
- const content = await readFile5(filePath, "utf-8");
8970
+ const filePath = path8.join(sessionDir, file);
8971
+ const content = await readFile6(filePath, "utf-8");
8549
8972
  const parsed = this.parseSummaryFile(content, sessionKey, file);
8550
8973
  summaries.push(...parsed);
8551
8974
  }
@@ -8633,22 +9056,22 @@ ${truncatedConversation}`;
8633
9056
  }
8634
9057
  // Get list of active sessions from transcript directory
8635
9058
  async getActiveSessions() {
8636
- const transcriptDir = path7.join(this.config.memoryDir, "transcripts");
9059
+ const transcriptDir = path8.join(this.config.memoryDir, "transcripts");
8637
9060
  try {
8638
9061
  const sessionKeys = /* @__PURE__ */ new Set();
8639
- const typeEntries = await readdir4(transcriptDir, { withFileTypes: true });
9062
+ const typeEntries = await readdir5(transcriptDir, { withFileTypes: true });
8640
9063
  for (const typeEnt of typeEntries) {
8641
9064
  if (!typeEnt.isDirectory()) continue;
8642
- const typeDir = path7.join(transcriptDir, typeEnt.name);
8643
- const idEntries = await readdir4(typeDir, { withFileTypes: true });
9065
+ const typeDir = path8.join(transcriptDir, typeEnt.name);
9066
+ const idEntries = await readdir5(typeDir, { withFileTypes: true });
8644
9067
  for (const idEnt of idEntries) {
8645
9068
  if (!idEnt.isDirectory()) continue;
8646
- const chanDir = path7.join(typeDir, idEnt.name);
8647
- const files = (await readdir4(chanDir)).filter((f) => f.endsWith(".jsonl")).sort();
9069
+ const chanDir = path8.join(typeDir, idEnt.name);
9070
+ const files = (await readdir5(chanDir)).filter((f) => f.endsWith(".jsonl")).sort();
8648
9071
  const last = files[files.length - 1];
8649
9072
  if (!last) continue;
8650
9073
  try {
8651
- const raw = await readFile5(path7.join(chanDir, last), "utf-8");
9074
+ const raw = await readFile6(path8.join(chanDir, last), "utf-8");
8652
9075
  const firstLine = raw.split("\n").find((l) => l.trim().length > 0);
8653
9076
  if (!firstLine) continue;
8654
9077
  const entry = JSON.parse(firstLine);
@@ -8683,15 +9106,15 @@ ${truncatedConversation}`;
8683
9106
  channelId = parts[3];
8684
9107
  }
8685
9108
  }
8686
- const transcriptDir = path7.join(this.config.memoryDir, "transcripts", channelType, channelId);
9109
+ const transcriptDir = path8.join(this.config.memoryDir, "transcripts", channelType, channelId);
8687
9110
  try {
8688
- const files = await readdir4(transcriptDir);
9111
+ const files = await readdir5(transcriptDir);
8689
9112
  const entries = [];
8690
9113
  for (const file of files) {
8691
9114
  if (!file.endsWith(".jsonl")) continue;
8692
- const transcriptPath = path7.join(transcriptDir, file);
9115
+ const transcriptPath = path8.join(transcriptDir, file);
8693
9116
  try {
8694
- const content = await readFile5(transcriptPath, "utf-8");
9117
+ const content = await readFile6(transcriptPath, "utf-8");
8695
9118
  const lines = content.trim().split("\n");
8696
9119
  for (const line of lines) {
8697
9120
  if (!line.trim()) continue;
@@ -9011,17 +9434,17 @@ async function rerankLocalOrNoop(opts) {
9011
9434
  }
9012
9435
 
9013
9436
  // src/relevance.ts
9014
- import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
9015
- import path8 from "path";
9437
+ import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
9438
+ import path9 from "path";
9016
9439
  var RelevanceStore = class {
9017
9440
  statePath;
9018
9441
  state = {};
9019
9442
  constructor(memoryDir) {
9020
- this.statePath = path8.join(memoryDir, "state", "relevance.json");
9443
+ this.statePath = path9.join(memoryDir, "state", "relevance.json");
9021
9444
  }
9022
9445
  async load() {
9023
9446
  try {
9024
- const raw = await readFile6(this.statePath, "utf-8");
9447
+ const raw = await readFile7(this.statePath, "utf-8");
9025
9448
  const parsed = JSON.parse(raw);
9026
9449
  if (parsed && typeof parsed === "object") this.state = parsed;
9027
9450
  } catch {
@@ -9043,8 +9466,8 @@ var RelevanceStore = class {
9043
9466
  };
9044
9467
  this.state[memoryId] = next;
9045
9468
  try {
9046
- await mkdir6(path8.dirname(this.statePath), { recursive: true });
9047
- await writeFile6(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
9469
+ await mkdir6(path9.dirname(this.statePath), { recursive: true });
9470
+ await writeFile7(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
9048
9471
  } catch (err) {
9049
9472
  log.debug(`relevance store write failed: ${err}`);
9050
9473
  }
@@ -9063,17 +9486,17 @@ var RelevanceStore = class {
9063
9486
  };
9064
9487
 
9065
9488
  // src/negative.ts
9066
- import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
9067
- import path9 from "path";
9489
+ import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
9490
+ import path10 from "path";
9068
9491
  var NegativeExampleStore = class {
9069
9492
  statePath;
9070
9493
  state = {};
9071
9494
  constructor(memoryDir) {
9072
- this.statePath = path9.join(memoryDir, "state", "negative_examples.json");
9495
+ this.statePath = path10.join(memoryDir, "state", "negative_examples.json");
9073
9496
  }
9074
9497
  async load() {
9075
9498
  try {
9076
- const raw = await readFile7(this.statePath, "utf-8");
9499
+ const raw = await readFile8(this.statePath, "utf-8");
9077
9500
  const parsed = JSON.parse(raw);
9078
9501
  if (parsed && typeof parsed === "object") this.state = parsed;
9079
9502
  } catch {
@@ -9096,8 +9519,8 @@ var NegativeExampleStore = class {
9096
9519
  this.state[memoryId] = next;
9097
9520
  }
9098
9521
  try {
9099
- await mkdir7(path9.dirname(this.statePath), { recursive: true });
9100
- await writeFile7(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
9522
+ await mkdir7(path10.dirname(this.statePath), { recursive: true });
9523
+ await writeFile8(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
9101
9524
  } catch (err) {
9102
9525
  log.debug(`negative example store write failed: ${err}`);
9103
9526
  }
@@ -9116,8 +9539,8 @@ var NegativeExampleStore = class {
9116
9539
  };
9117
9540
 
9118
9541
  // src/recall-state.ts
9119
- import { appendFile as appendFile3, mkdir as mkdir8, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
9120
- import path10 from "path";
9542
+ import { appendFile as appendFile3, mkdir as mkdir8, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
9543
+ import path11 from "path";
9121
9544
  import { createHash as createHash2 } from "crypto";
9122
9545
  function clampGraphRecallExpandedEntries(entries, maxEntries = 64) {
9123
9546
  const limit = Math.max(1, Math.floor(maxEntries));
@@ -9152,12 +9575,12 @@ var LastRecallStore = class {
9152
9575
  impressionsPath;
9153
9576
  state = {};
9154
9577
  constructor(memoryDir) {
9155
- this.statePath = path10.join(memoryDir, "state", "last_recall.json");
9156
- this.impressionsPath = path10.join(memoryDir, "state", "recall_impressions.jsonl");
9578
+ this.statePath = path11.join(memoryDir, "state", "last_recall.json");
9579
+ this.impressionsPath = path11.join(memoryDir, "state", "recall_impressions.jsonl");
9157
9580
  }
9158
9581
  async load() {
9159
9582
  try {
9160
- const raw = await readFile8(this.statePath, "utf-8");
9583
+ const raw = await readFile9(this.statePath, "utf-8");
9161
9584
  const parsed = JSON.parse(raw);
9162
9585
  if (parsed && typeof parsed === "object") this.state = parsed;
9163
9586
  } catch {
@@ -9200,13 +9623,13 @@ var LastRecallStore = class {
9200
9623
  }
9201
9624
  }
9202
9625
  try {
9203
- await mkdir8(path10.dirname(this.statePath), { recursive: true });
9204
- await writeFile8(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
9626
+ await mkdir8(path11.dirname(this.statePath), { recursive: true });
9627
+ await writeFile9(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
9205
9628
  } catch (err) {
9206
9629
  log.debug(`last recall store write failed: ${err}`);
9207
9630
  }
9208
9631
  try {
9209
- await mkdir8(path10.dirname(this.impressionsPath), { recursive: true });
9632
+ await mkdir8(path11.dirname(this.impressionsPath), { recursive: true });
9210
9633
  await appendFile3(this.impressionsPath, JSON.stringify(snapshot) + "\n", "utf-8");
9211
9634
  } catch (err) {
9212
9635
  log.debug(`recall impressions append failed: ${err}`);
@@ -9217,11 +9640,11 @@ var TierMigrationStatusStore = class {
9217
9640
  statePath;
9218
9641
  state = structuredClone(DEFAULT_TIER_MIGRATION_STATUS);
9219
9642
  constructor(memoryDir) {
9220
- this.statePath = path10.join(memoryDir, "state", "tier-migration-status.json");
9643
+ this.statePath = path11.join(memoryDir, "state", "tier-migration-status.json");
9221
9644
  }
9222
9645
  async load() {
9223
9646
  try {
9224
- const raw = await readFile8(this.statePath, "utf-8");
9647
+ const raw = await readFile9(this.statePath, "utf-8");
9225
9648
  const parsed = JSON.parse(raw);
9226
9649
  if (!parsed || typeof parsed !== "object") {
9227
9650
  this.state = structuredClone(DEFAULT_TIER_MIGRATION_STATUS);
@@ -9270,8 +9693,8 @@ var TierMigrationStatusStore = class {
9270
9693
  };
9271
9694
  this.state = next;
9272
9695
  try {
9273
- await mkdir8(path10.dirname(this.statePath), { recursive: true });
9274
- await writeFile8(this.statePath, JSON.stringify(next, null, 2), "utf-8");
9696
+ await mkdir8(path11.dirname(this.statePath), { recursive: true });
9697
+ await writeFile9(this.statePath, JSON.stringify(next, null, 2), "utf-8");
9275
9698
  } catch (err) {
9276
9699
  log.debug(`tier migration status write failed: ${err}`);
9277
9700
  }
@@ -9279,8 +9702,8 @@ var TierMigrationStatusStore = class {
9279
9702
  };
9280
9703
 
9281
9704
  // src/session-observer-state.ts
9282
- import path11 from "path";
9283
- import { mkdir as mkdir9, open, readFile as readFile9, stat as stat3, unlink as unlink3, writeFile as writeFile9 } from "fs/promises";
9705
+ import path12 from "path";
9706
+ import { mkdir as mkdir9, open, readFile as readFile10, stat as stat3, unlink as unlink4, writeFile as writeFile10 } from "fs/promises";
9284
9707
  function sanitizeNonNegativeInt(value) {
9285
9708
  if (!Number.isFinite(value)) return 0;
9286
9709
  return Math.max(0, Math.floor(value));
@@ -9356,7 +9779,7 @@ var SessionObserverState = class {
9356
9779
  saveQueue = Promise.resolve();
9357
9780
  async readPersistedState() {
9358
9781
  try {
9359
- const raw = await readFile9(this.statePath, "utf-8");
9782
+ const raw = await readFile10(this.statePath, "utf-8");
9360
9783
  const parsed = JSON.parse(raw);
9361
9784
  if (parsed?.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object") {
9362
9785
  return null;
@@ -9382,13 +9805,13 @@ var SessionObserverState = class {
9382
9805
  return next;
9383
9806
  }
9384
9807
  constructor(opts) {
9385
- this.statePath = path11.join(opts.memoryDir, "state", "session-observer-state.json");
9386
- this.lockPath = path11.join(opts.memoryDir, "state", "session-observer-state.lock");
9808
+ this.statePath = path12.join(opts.memoryDir, "state", "session-observer-state.json");
9809
+ this.lockPath = path12.join(opts.memoryDir, "state", "session-observer-state.lock");
9387
9810
  this.debounceMs = Math.max(0, Math.floor(opts.debounceMs));
9388
9811
  this.bands = normalizeObserverBands(opts.bands);
9389
9812
  }
9390
9813
  async withSaveLock(fn) {
9391
- await mkdir9(path11.dirname(this.lockPath), { recursive: true });
9814
+ await mkdir9(path12.dirname(this.lockPath), { recursive: true });
9392
9815
  for (let attempt = 0; attempt < 80; attempt++) {
9393
9816
  try {
9394
9817
  const handle = await open(this.lockPath, "wx");
@@ -9396,7 +9819,7 @@ var SessionObserverState = class {
9396
9819
  await fn();
9397
9820
  } finally {
9398
9821
  await handle.close();
9399
- await unlink3(this.lockPath).catch(() => {
9822
+ await unlink4(this.lockPath).catch(() => {
9400
9823
  });
9401
9824
  }
9402
9825
  return;
@@ -9405,7 +9828,7 @@ var SessionObserverState = class {
9405
9828
  try {
9406
9829
  const lockInfo = await stat3(this.lockPath);
9407
9830
  if (Date.now() - lockInfo.mtimeMs > this.lockStaleMs) {
9408
- await unlink3(this.lockPath).catch(() => {
9831
+ await unlink4(this.lockPath).catch(() => {
9409
9832
  });
9410
9833
  continue;
9411
9834
  }
@@ -9449,8 +9872,8 @@ var SessionObserverState = class {
9449
9872
  sessions[key] = value;
9450
9873
  }
9451
9874
  const payload = { version: 1, sessions };
9452
- await mkdir9(path11.dirname(this.statePath), { recursive: true });
9453
- await writeFile9(this.statePath, JSON.stringify(payload, null, 2), "utf-8");
9875
+ await mkdir9(path12.dirname(this.statePath), { recursive: true });
9876
+ await writeFile10(this.statePath, JSON.stringify(payload, null, 2), "utf-8");
9454
9877
  });
9455
9878
  }
9456
9879
  enqueueSave() {
@@ -9543,13 +9966,13 @@ var SessionObserverState = class {
9543
9966
  };
9544
9967
 
9545
9968
  // src/embedding-fallback.ts
9546
- import path12 from "path";
9547
- import { mkdir as mkdir10, readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
9969
+ import path13 from "path";
9970
+ import { mkdir as mkdir10, readFile as readFile11, writeFile as writeFile11 } from "fs/promises";
9548
9971
  var DEFAULT_OPENAI_MODEL = "text-embedding-3-small";
9549
9972
  var EmbeddingFallback = class {
9550
9973
  constructor(config) {
9551
9974
  this.config = config;
9552
- this.indexPath = path12.join(config.memoryDir, "state", "embeddings.json");
9975
+ this.indexPath = path13.join(config.memoryDir, "state", "embeddings.json");
9553
9976
  }
9554
9977
  indexPath;
9555
9978
  loaded = null;
@@ -9659,7 +10082,7 @@ var EmbeddingFallback = class {
9659
10082
  return this.loaded;
9660
10083
  }
9661
10084
  try {
9662
- const raw = await readFile10(this.indexPath, "utf-8");
10085
+ const raw = await readFile11(this.indexPath, "utf-8");
9663
10086
  const parsed = JSON.parse(raw);
9664
10087
  if (parsed && parsed.version === 1 && parsed.entries && typeof parsed.entries === "object") {
9665
10088
  this.loaded = {
@@ -9681,14 +10104,14 @@ var EmbeddingFallback = class {
9681
10104
  return this.loaded;
9682
10105
  }
9683
10106
  async saveIndex(index) {
9684
- await mkdir10(path12.dirname(this.indexPath), { recursive: true });
9685
- await writeFile10(this.indexPath, JSON.stringify(index), "utf-8");
10107
+ await mkdir10(path13.dirname(this.indexPath), { recursive: true });
10108
+ await writeFile11(this.indexPath, JSON.stringify(index), "utf-8");
9686
10109
  this.loaded = index;
9687
10110
  }
9688
10111
  };
9689
10112
  function toMemoryRelativePath(memoryDir, filePath) {
9690
- if (!path12.isAbsolute(filePath)) return filePath;
9691
- const rel = path12.relative(memoryDir, filePath);
10113
+ if (!path13.isAbsolute(filePath)) return filePath;
10114
+ const rel = path13.relative(memoryDir, filePath);
9692
10115
  return rel.startsWith("..") ? filePath : rel;
9693
10116
  }
9694
10117
  function cosineSimilarity(a, b) {
@@ -9710,8 +10133,8 @@ function cosineSimilarity(a, b) {
9710
10133
  }
9711
10134
 
9712
10135
  // src/bootstrap.ts
9713
- import path13 from "path";
9714
- import { readdir as readdir5, readFile as readFile11 } from "fs/promises";
10136
+ import path14 from "path";
10137
+ import { readdir as readdir6, readFile as readFile12 } from "fs/promises";
9715
10138
  var BootstrapEngine = class {
9716
10139
  constructor(config, orchestrator) {
9717
10140
  this.config = config;
@@ -9790,7 +10213,7 @@ var BootstrapEngine = class {
9790
10213
  for (const filePath of files) {
9791
10214
  let raw = "";
9792
10215
  try {
9793
- raw = await readFile11(filePath, "utf-8");
10216
+ raw = await readFile12(filePath, "utf-8");
9794
10217
  } catch {
9795
10218
  continue;
9796
10219
  }
@@ -9808,7 +10231,7 @@ var BootstrapEngine = class {
9808
10231
  const role = String(parsed?.role ?? "");
9809
10232
  const content = typeof parsed?.content === "string" ? parsed.content : "";
9810
10233
  if (!role || !content) continue;
9811
- const sessionKey = typeof parsed?.sessionKey === "string" && parsed.sessionKey.length > 0 ? parsed.sessionKey : path13.relative(baseDir, filePath);
10234
+ const sessionKey = typeof parsed?.sessionKey === "string" && parsed.sessionKey.length > 0 ? parsed.sessionKey : path14.relative(baseDir, filePath);
9812
10235
  const list = bySession.get(sessionKey) ?? [];
9813
10236
  list.push({
9814
10237
  role,
@@ -9826,9 +10249,9 @@ var BootstrapEngine = class {
9826
10249
  }
9827
10250
  async listJsonlFiles(dir) {
9828
10251
  const out = [];
9829
- const entries = await readdir5(dir, { withFileTypes: true }).catch(() => []);
10252
+ const entries = await readdir6(dir, { withFileTypes: true }).catch(() => []);
9830
10253
  for (const entry of entries) {
9831
- const full = path13.join(dir, entry.name);
10254
+ const full = path14.join(dir, entry.name);
9832
10255
  if (entry.isDirectory()) {
9833
10256
  out.push(...await this.listJsonlFiles(full));
9834
10257
  } else if (entry.isFile() && full.endsWith(".jsonl")) {
@@ -10343,8 +10766,8 @@ function renderCompressionGuidelinesMarkdown(candidate) {
10343
10766
  }
10344
10767
 
10345
10768
  // src/boxes.ts
10346
- import { mkdir as mkdir11, writeFile as writeFile11, readFile as readFile12, readdir as readdir6 } from "fs/promises";
10347
- import path14 from "path";
10769
+ import { mkdir as mkdir11, writeFile as writeFile12, readFile as readFile13, readdir as readdir7 } from "fs/promises";
10770
+ import path15 from "path";
10348
10771
  import { createHash as createHash3 } from "crypto";
10349
10772
  var BOX_DIR = "boxes";
10350
10773
  var STATE_DIR = "state";
@@ -10419,23 +10842,23 @@ var BoxBuilder = class {
10419
10842
  this.cfg = cfg;
10420
10843
  }
10421
10844
  get boxBaseDir() {
10422
- return path14.join(this.baseDir, BOX_DIR);
10845
+ return path15.join(this.baseDir, BOX_DIR);
10423
10846
  }
10424
10847
  get stateDir() {
10425
- return path14.join(this.baseDir, STATE_DIR);
10848
+ return path15.join(this.baseDir, STATE_DIR);
10426
10849
  }
10427
10850
  get openBoxStatePath() {
10428
- return path14.join(this.stateDir, OPEN_BOX_STATE_FILE);
10851
+ return path15.join(this.stateDir, OPEN_BOX_STATE_FILE);
10429
10852
  }
10430
10853
  get tracesPath() {
10431
- return path14.join(this.stateDir, TRACES_FILE);
10854
+ return path15.join(this.stateDir, TRACES_FILE);
10432
10855
  }
10433
10856
  // ── State persistence ────────────────────────────────────────────────────
10434
10857
  async loadOpenBox() {
10435
10858
  if (this.stateLoaded) return;
10436
10859
  this.stateLoaded = true;
10437
10860
  try {
10438
- const raw = await readFile12(this.openBoxStatePath, "utf-8");
10861
+ const raw = await readFile13(this.openBoxStatePath, "utf-8");
10439
10862
  this.openBox = JSON.parse(raw);
10440
10863
  } catch {
10441
10864
  this.openBox = null;
@@ -10444,17 +10867,17 @@ var BoxBuilder = class {
10444
10867
  async saveOpenBox() {
10445
10868
  await mkdir11(this.stateDir, { recursive: true });
10446
10869
  if (this.openBox) {
10447
- await writeFile11(this.openBoxStatePath, JSON.stringify(this.openBox, null, 2), "utf-8");
10870
+ await writeFile12(this.openBoxStatePath, JSON.stringify(this.openBox, null, 2), "utf-8");
10448
10871
  } else {
10449
10872
  try {
10450
- await writeFile11(this.openBoxStatePath, "null", "utf-8");
10873
+ await writeFile12(this.openBoxStatePath, "null", "utf-8");
10451
10874
  } catch {
10452
10875
  }
10453
10876
  }
10454
10877
  }
10455
10878
  async loadTraceIndex() {
10456
10879
  try {
10457
- const raw = await readFile12(this.tracesPath, "utf-8");
10880
+ const raw = await readFile13(this.tracesPath, "utf-8");
10458
10881
  const parsed = JSON.parse(raw);
10459
10882
  parsed.traceLastSeen ??= {};
10460
10883
  return parsed;
@@ -10465,7 +10888,7 @@ var BoxBuilder = class {
10465
10888
  async saveTraceIndex(idx) {
10466
10889
  try {
10467
10890
  await mkdir11(this.stateDir, { recursive: true });
10468
- await writeFile11(this.tracesPath, JSON.stringify(idx, null, 2), "utf-8");
10891
+ await writeFile12(this.tracesPath, JSON.stringify(idx, null, 2), "utf-8");
10469
10892
  } catch (err) {
10470
10893
  log.warn(`[engram/boxes] Failed to save trace index: ${err.message}`);
10471
10894
  }
@@ -10541,7 +10964,7 @@ var BoxBuilder = class {
10541
10964
  }
10542
10965
  const sealedAt = (/* @__PURE__ */ new Date()).toISOString();
10543
10966
  const day = sealedAt.slice(0, 10);
10544
- const dir = path14.join(this.boxBaseDir, day);
10967
+ const dir = path15.join(this.boxBaseDir, day);
10545
10968
  await mkdir11(dir, { recursive: true });
10546
10969
  let traceId;
10547
10970
  if (this.cfg.traceWeaverEnabled && box.topics.length > 0) {
@@ -10561,8 +10984,8 @@ var BoxBuilder = class {
10561
10984
 
10562
10985
  <!-- Topics: ${box.topics.join(", ")} | Memories: ${box.memoryIds.length} -->
10563
10986
  `;
10564
- const filePath = path14.join(dir, `${box.id}.md`);
10565
- await writeFile11(filePath, content, "utf-8");
10987
+ const filePath = path15.join(dir, `${box.id}.md`);
10988
+ await writeFile12(filePath, content, "utf-8");
10566
10989
  log.debug(`[boxes] sealed box ${box.id} (${reason}): ${box.memoryIds.length} memories, topics=[${box.topics.join(",")}]`);
10567
10990
  await this.saveOpenBox();
10568
10991
  return box.id;
@@ -10611,14 +11034,14 @@ var BoxBuilder = class {
10611
11034
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
10612
11035
  const walkDir = async (dir) => {
10613
11036
  try {
10614
- const entries = await readdir6(dir, { withFileTypes: true });
11037
+ const entries = await readdir7(dir, { withFileTypes: true });
10615
11038
  for (const e of entries) {
10616
- const full = path14.join(dir, e.name);
11039
+ const full = path15.join(dir, e.name);
10617
11040
  if (e.isDirectory()) {
10618
11041
  await walkDir(full);
10619
11042
  } else if (e.name.endsWith(".md")) {
10620
11043
  try {
10621
- const raw = await readFile12(full, "utf-8");
11044
+ const raw = await readFile13(full, "utf-8");
10622
11045
  const parsed = parseBoxFrontmatter(raw);
10623
11046
  if (parsed && new Date(parsed.sealedAt) >= cutoff) {
10624
11047
  boxes.push(parsed);
@@ -10726,8 +11149,8 @@ function classifyMemoryKind(content, tags, category) {
10726
11149
 
10727
11150
  // src/tmt.ts
10728
11151
  import * as fs from "fs";
10729
- import * as path15 from "path";
10730
- import { mkdir as mkdir12, readFile as readFile13, writeFile as writeFile12, readdir as readdir7 } from "fs/promises";
11152
+ import * as path16 from "path";
11153
+ import { mkdir as mkdir12, readFile as readFile14, writeFile as writeFile13, readdir as readdir8 } from "fs/promises";
10731
11154
  var TMT_DIR = "tmt";
10732
11155
  var TMT_LEVEL_INPUT_LIMITS = {
10733
11156
  hour: { totalChars: 48e3, itemChars: 2e3, maxItems: 64 },
@@ -10756,19 +11179,19 @@ function capTmtSummaryInputs(inputs, level) {
10756
11179
  return [fallback.length > itemChars ? `${fallback.slice(0, itemChars - 1)}\u2026` : fallback];
10757
11180
  }
10758
11181
  function tmtDir(baseDir) {
10759
- return path15.join(baseDir, TMT_DIR);
11182
+ return path16.join(baseDir, TMT_DIR);
10760
11183
  }
10761
11184
  function hourNodePath(baseDir, date, hour) {
10762
- return path15.join(tmtDir(baseDir), date, `hour-${hour}.md`);
11185
+ return path16.join(tmtDir(baseDir), date, `hour-${hour}.md`);
10763
11186
  }
10764
11187
  function dayNodePath(baseDir, date) {
10765
- return path15.join(tmtDir(baseDir), date, "day.md");
11188
+ return path16.join(tmtDir(baseDir), date, "day.md");
10766
11189
  }
10767
11190
  function weekNodePath(baseDir, weekKey) {
10768
- return path15.join(tmtDir(baseDir), `week-${weekKey}.md`);
11191
+ return path16.join(tmtDir(baseDir), `week-${weekKey}.md`);
10769
11192
  }
10770
11193
  function personaNodePath(baseDir) {
10771
- return path15.join(tmtDir(baseDir), "persona.md");
11194
+ return path16.join(tmtDir(baseDir), "persona.md");
10772
11195
  }
10773
11196
  function serialiseTmtNode(fm, summary) {
10774
11197
  const yaml = [
@@ -10839,7 +11262,7 @@ var TmtBuilder = class {
10839
11262
  let shouldBuild = !fs.existsSync(nodePath);
10840
11263
  if (!shouldBuild) {
10841
11264
  try {
10842
- const existing = await readFile13(nodePath, "utf8");
11265
+ const existing = await readFile14(nodePath, "utf8");
10843
11266
  const countMatch = existing.match(/memoryCount: (\d+)/);
10844
11267
  if (!countMatch || parseInt(countMatch[1], 10) < entries.length) {
10845
11268
  shouldBuild = true;
@@ -10865,8 +11288,8 @@ var TmtBuilder = class {
10865
11288
  sourceIds: entries.map((e) => e.id),
10866
11289
  builtAt: (/* @__PURE__ */ new Date()).toISOString()
10867
11290
  };
10868
- await mkdir12(path15.dirname(nodePath), { recursive: true });
10869
- await writeFile12(nodePath, serialiseTmtNode(fm, summary), "utf8");
11291
+ await mkdir12(path16.dirname(nodePath), { recursive: true });
11292
+ await writeFile13(nodePath, serialiseTmtNode(fm, summary), "utf8");
10870
11293
  }
10871
11294
  }
10872
11295
  async buildDayNodes(memories, summarize2) {
@@ -10881,7 +11304,7 @@ var TmtBuilder = class {
10881
11304
  let shouldBuild = !fs.existsSync(nodePath);
10882
11305
  if (!shouldBuild) {
10883
11306
  try {
10884
- const existing = await readFile13(nodePath, "utf8");
11307
+ const existing = await readFile14(nodePath, "utf8");
10885
11308
  const countMatch = existing.match(/memoryCount: (\d+)/);
10886
11309
  if (!countMatch || parseInt(countMatch[1], 10) < entries.length) {
10887
11310
  shouldBuild = true;
@@ -10902,7 +11325,7 @@ var TmtBuilder = class {
10902
11325
  const hPath = hourNodePath(this.baseDir, date, h);
10903
11326
  if (fs.existsSync(hPath)) {
10904
11327
  try {
10905
- const hContent = await readFile13(hPath, "utf8");
11328
+ const hContent = await readFile14(hPath, "utf8");
10906
11329
  const hSummary = hContent.replace(/^---[\s\S]*?---\n\n?/, "").trim();
10907
11330
  if (hSummary) {
10908
11331
  inputs.push(hSummary);
@@ -10930,8 +11353,8 @@ var TmtBuilder = class {
10930
11353
  sourceIds: entries.map((e) => e.id),
10931
11354
  builtAt: (/* @__PURE__ */ new Date()).toISOString()
10932
11355
  };
10933
- await mkdir12(path15.dirname(nodePath), { recursive: true });
10934
- await writeFile12(nodePath, serialiseTmtNode(fm, summary), "utf8");
11356
+ await mkdir12(path16.dirname(nodePath), { recursive: true });
11357
+ await writeFile13(nodePath, serialiseTmtNode(fm, summary), "utf8");
10935
11358
  }
10936
11359
  }
10937
11360
  /**
@@ -10952,7 +11375,7 @@ var TmtBuilder = class {
10952
11375
  let shouldBuild = !fs.existsSync(nodePath);
10953
11376
  if (!shouldBuild) {
10954
11377
  try {
10955
- const existing = await readFile13(nodePath, "utf8");
11378
+ const existing = await readFile14(nodePath, "utf8");
10956
11379
  const countMatch = existing.match(/memoryCount: (\d+)/);
10957
11380
  if (!countMatch || parseInt(countMatch[1], 10) < entries.length) {
10958
11381
  shouldBuild = true;
@@ -10966,7 +11389,7 @@ var TmtBuilder = class {
10966
11389
  const dir = tmtDir(this.baseDir);
10967
11390
  let allDirs = [];
10968
11391
  try {
10969
- allDirs = await readdir7(dir);
11392
+ allDirs = await readdir8(dir);
10970
11393
  } catch {
10971
11394
  continue;
10972
11395
  }
@@ -10978,7 +11401,7 @@ var TmtBuilder = class {
10978
11401
  const dayPath = dayNodePath(this.baseDir, dateDir);
10979
11402
  if (fs.existsSync(dayPath)) {
10980
11403
  try {
10981
- const content = await readFile13(dayPath, "utf8");
11404
+ const content = await readFile14(dayPath, "utf8");
10982
11405
  const summary2 = content.replace(/^---[\s\S]*?---\n\n?/, "").trim();
10983
11406
  if (summary2) daySummaries.push(summary2);
10984
11407
  } catch {
@@ -10996,8 +11419,8 @@ var TmtBuilder = class {
10996
11419
  sourceIds: entries.map((e) => e.id),
10997
11420
  builtAt: (/* @__PURE__ */ new Date()).toISOString()
10998
11421
  };
10999
- await mkdir12(path15.dirname(nodePath), { recursive: true });
11000
- await writeFile12(nodePath, serialiseTmtNode(fm, summary), "utf8");
11422
+ await mkdir12(path16.dirname(nodePath), { recursive: true });
11423
+ await writeFile13(nodePath, serialiseTmtNode(fm, summary), "utf8");
11001
11424
  } catch (err) {
11002
11425
  console.warn(`[engram] tmt: week node build failed for ${week} (ignored): ${err}`);
11003
11426
  }
@@ -11013,7 +11436,7 @@ var TmtBuilder = class {
11013
11436
  const dir = tmtDir(this.baseDir);
11014
11437
  let allFiles = [];
11015
11438
  try {
11016
- allFiles = await readdir7(dir);
11439
+ allFiles = await readdir8(dir);
11017
11440
  } catch {
11018
11441
  return;
11019
11442
  }
@@ -11025,7 +11448,7 @@ var TmtBuilder = class {
11025
11448
  let latestEnd;
11026
11449
  for (const f of weekFiles) {
11027
11450
  try {
11028
- const content = await readFile13(path15.join(dir, f), "utf8");
11451
+ const content = await readFile14(path16.join(dir, f), "utf8");
11029
11452
  const summary2 = content.replace(/^---[\s\S]*?---\n\n?/, "").trim();
11030
11453
  if (summary2) weekSummaries.push(summary2);
11031
11454
  const countMatch = content.match(/memoryCount: (\d+)/);
@@ -11046,7 +11469,7 @@ var TmtBuilder = class {
11046
11469
  let shouldBuild = !fs.existsSync(nodePath);
11047
11470
  if (!shouldBuild) {
11048
11471
  try {
11049
- const existing = await readFile13(nodePath, "utf8");
11472
+ const existing = await readFile14(nodePath, "utf8");
11050
11473
  const countMatch = existing.match(/memoryCount: (\d+)/);
11051
11474
  if (!countMatch || parseInt(countMatch[1], 10) !== totalCount) {
11052
11475
  shouldBuild = true;
@@ -11066,7 +11489,7 @@ var TmtBuilder = class {
11066
11489
  sourceIds: [],
11067
11490
  builtAt: now
11068
11491
  };
11069
- await writeFile12(nodePath, serialiseTmtNode(fm, summary), "utf8");
11492
+ await writeFile13(nodePath, serialiseTmtNode(fm, summary), "utf8");
11070
11493
  } catch (err) {
11071
11494
  console.warn(`[engram] tmt: persona node build failed (ignored): ${err}`);
11072
11495
  }
@@ -11084,13 +11507,13 @@ var TmtBuilder = class {
11084
11507
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
11085
11508
  const todayDay = dayNodePath(this.baseDir, today);
11086
11509
  if (fs.existsSync(todayDay)) {
11087
- const content = await readFile13(todayDay, "utf8");
11510
+ const content = await readFile14(todayDay, "utf8");
11088
11511
  const summary = content.replace(/^---[\s\S]*?---\n\n?/, "").trim();
11089
11512
  if (summary) return { level: "day", summary };
11090
11513
  }
11091
11514
  let entries = [];
11092
11515
  try {
11093
- entries = await readdir7(dir);
11516
+ entries = await readdir8(dir);
11094
11517
  } catch {
11095
11518
  return null;
11096
11519
  }
@@ -11098,7 +11521,7 @@ var TmtBuilder = class {
11098
11521
  for (const dateDir of dateDirs) {
11099
11522
  const dayPath = dayNodePath(this.baseDir, dateDir);
11100
11523
  if (fs.existsSync(dayPath)) {
11101
- const content = await readFile13(dayPath, "utf8");
11524
+ const content = await readFile14(dayPath, "utf8");
11102
11525
  const summary = content.replace(/^---[\s\S]*?---\n\n?/, "").trim();
11103
11526
  if (summary) return { level: "day", summary };
11104
11527
  }
@@ -11112,18 +11535,18 @@ var TmtBuilder = class {
11112
11535
 
11113
11536
  // src/temporal-index.ts
11114
11537
  import * as fs2 from "fs";
11115
- import * as path16 from "path";
11538
+ import * as path17 from "path";
11116
11539
  var INDEX_VERSION = 1;
11117
11540
  var TEMPORAL_INDEX_FILE = "index_time.json";
11118
11541
  var TAG_INDEX_FILE = "index_tags.json";
11119
11542
  function stateDir(memoryDir) {
11120
- return path16.join(memoryDir, "state");
11543
+ return path17.join(memoryDir, "state");
11121
11544
  }
11122
11545
  function temporalIndexPath(memoryDir) {
11123
- return path16.join(stateDir(memoryDir), TEMPORAL_INDEX_FILE);
11546
+ return path17.join(stateDir(memoryDir), TEMPORAL_INDEX_FILE);
11124
11547
  }
11125
11548
  function tagIndexPath(memoryDir) {
11126
- return path16.join(stateDir(memoryDir), TAG_INDEX_FILE);
11549
+ return path17.join(stateDir(memoryDir), TAG_INDEX_FILE);
11127
11550
  }
11128
11551
  function ensureStateDir(memoryDir) {
11129
11552
  const dir = stateDir(memoryDir);
@@ -11357,8 +11780,8 @@ function recencyWindowFromPrompt(prompt, nowMs = Date.now()) {
11357
11780
  }
11358
11781
 
11359
11782
  // src/graph.ts
11360
- import { mkdir as mkdir13, appendFile as appendFile4, readFile as readFile14 } from "fs/promises";
11361
- import * as path17 from "path";
11783
+ import { mkdir as mkdir13, appendFile as appendFile4, readFile as readFile15 } from "fs/promises";
11784
+ import * as path18 from "path";
11362
11785
  var CAUSAL_PHRASES = [
11363
11786
  "as a result",
11364
11787
  "led to",
@@ -11368,10 +11791,10 @@ var CAUSAL_PHRASES = [
11368
11791
  "because"
11369
11792
  ];
11370
11793
  function graphsDir(memoryDir) {
11371
- return path17.join(memoryDir, "state", "graphs");
11794
+ return path18.join(memoryDir, "state", "graphs");
11372
11795
  }
11373
11796
  function graphFilePath(memoryDir, type) {
11374
- return path17.join(graphsDir(memoryDir), `${type}.jsonl`);
11797
+ return path18.join(graphsDir(memoryDir), `${type}.jsonl`);
11375
11798
  }
11376
11799
  async function ensureGraphsDir(memoryDir) {
11377
11800
  await mkdir13(graphsDir(memoryDir), { recursive: true });
@@ -11384,7 +11807,7 @@ async function appendEdge(memoryDir, edge) {
11384
11807
  async function readEdges(memoryDir, type) {
11385
11808
  const filePath = graphFilePath(memoryDir, type);
11386
11809
  try {
11387
- const raw = await readFile14(filePath, "utf8");
11810
+ const raw = await readFile15(filePath, "utf8");
11388
11811
  const edges = [];
11389
11812
  for (const line of raw.split("\n")) {
11390
11813
  const trimmed = line.trim();
@@ -11427,7 +11850,7 @@ async function analyzeGraphHealth(memoryDir, options) {
11427
11850
  let corruptLines = 0;
11428
11851
  const nodes = /* @__PURE__ */ new Set();
11429
11852
  try {
11430
- const raw = await readFile14(filePath, "utf8");
11853
+ const raw = await readFile15(filePath, "utf8");
11431
11854
  for (const line of raw.split("\n")) {
11432
11855
  const trimmed = line.trim();
11433
11856
  if (!trimmed) continue;
@@ -11751,8 +12174,8 @@ function chunkTranscriptEntries(sessionKey, entries, opts) {
11751
12174
  }
11752
12175
 
11753
12176
  // src/conversation-index/indexer.ts
11754
- import { mkdir as mkdir14, writeFile as writeFile13 } from "fs/promises";
11755
- import path18 from "path";
12177
+ import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
12178
+ import path19 from "path";
11756
12179
  function sanitizeSessionKey(sessionKey) {
11757
12180
  const raw = typeof sessionKey === "string" && sessionKey.trim().length > 0 ? sessionKey : "unknown-session";
11758
12181
  return raw.toLowerCase().replace(/[^a-z0-9._-]+/g, "_").slice(0, 200);
@@ -11762,9 +12185,9 @@ async function writeConversationChunks(rootDir, chunks) {
11762
12185
  for (const c of chunks) {
11763
12186
  const safe = sanitizeSessionKey(c.sessionKey);
11764
12187
  const date = c.startTs.slice(0, 10);
11765
- const dir = path18.join(rootDir, safe, date);
12188
+ const dir = path19.join(rootDir, safe, date);
11766
12189
  await mkdir14(dir, { recursive: true });
11767
- const fp = path18.join(dir, `${c.id}.md`);
12190
+ const fp = path19.join(dir, `${c.id}.md`);
11768
12191
  const content = `---
11769
12192
  kind: conversation_chunk
11770
12193
  sessionKey: ${c.sessionKey}
@@ -11773,7 +12196,7 @@ endTs: ${c.endTs}
11773
12196
  ---
11774
12197
 
11775
12198
  ` + c.text + "\n";
11776
- await writeFile13(fp, content, "utf-8");
12199
+ await writeFile14(fp, content, "utf-8");
11777
12200
  written.push(fp);
11778
12201
  }
11779
12202
  return written;
@@ -11792,28 +12215,28 @@ async function upsertConversationChunksFailOpen(adapter, chunks) {
11792
12215
  }
11793
12216
 
11794
12217
  // src/conversation-index/cleanup.ts
11795
- import { readdir as readdir8, rm } from "fs/promises";
11796
- import path19 from "path";
12218
+ import { readdir as readdir9, rm } from "fs/promises";
12219
+ import path20 from "path";
11797
12220
  async function cleanupConversationChunks(rootDir, retentionDays) {
11798
12221
  if (!Number.isFinite(retentionDays) || retentionDays <= 0) return;
11799
12222
  const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
11800
12223
  try {
11801
- const sessions = await readdir8(rootDir, { withFileTypes: true });
12224
+ const sessions = await readdir9(rootDir, { withFileTypes: true });
11802
12225
  for (const s of sessions) {
11803
12226
  if (!s.isDirectory()) continue;
11804
- const sessionDir = path19.join(rootDir, s.name);
11805
- const dayDirs = await readdir8(sessionDir, { withFileTypes: true });
12227
+ const sessionDir = path20.join(rootDir, s.name);
12228
+ const dayDirs = await readdir9(sessionDir, { withFileTypes: true });
11806
12229
  for (const d of dayDirs) {
11807
12230
  if (!d.isDirectory()) continue;
11808
12231
  if (!/^\d{4}-\d{2}-\d{2}$/.test(d.name)) continue;
11809
12232
  const dayMs = (/* @__PURE__ */ new Date(d.name + "T00:00:00.000Z")).getTime();
11810
12233
  if (!Number.isFinite(dayMs)) continue;
11811
12234
  if (dayMs < cutoffMs) {
11812
- await rm(path19.join(sessionDir, d.name), { recursive: true, force: true });
12235
+ await rm(path20.join(sessionDir, d.name), { recursive: true, force: true });
11813
12236
  }
11814
12237
  }
11815
12238
  try {
11816
- const remaining = await readdir8(sessionDir);
12239
+ const remaining = await readdir9(sessionDir);
11817
12240
  if (remaining.length === 0) {
11818
12241
  await rm(sessionDir, { recursive: true, force: true });
11819
12242
  }
@@ -11828,7 +12251,7 @@ async function cleanupConversationChunks(rootDir, retentionDays) {
11828
12251
  // src/conversation-index/faiss-adapter.ts
11829
12252
  import * as childProcess from "child_process";
11830
12253
  import { fileURLToPath } from "url";
11831
- import path20 from "path";
12254
+ import path21 from "path";
11832
12255
  var FaissAdapterError = class extends Error {
11833
12256
  constructor(message, code) {
11834
12257
  super(message);
@@ -11838,18 +12261,18 @@ var FaissAdapterError = class extends Error {
11838
12261
  };
11839
12262
  function resolveDefaultFaissScriptPath(fromModuleUrl = import.meta.url) {
11840
12263
  const currentFile = fileURLToPath(fromModuleUrl);
11841
- const moduleDir = path20.dirname(currentFile);
11842
- if (moduleDir.endsWith(`${path20.sep}conversation-index`)) {
11843
- return path20.resolve(moduleDir, "..", "..", "scripts", "faiss_index.py");
12264
+ const moduleDir = path21.dirname(currentFile);
12265
+ if (moduleDir.endsWith(`${path21.sep}conversation-index`)) {
12266
+ return path21.resolve(moduleDir, "..", "..", "scripts", "faiss_index.py");
11844
12267
  }
11845
- return path20.resolve(moduleDir, "..", "scripts", "faiss_index.py");
12268
+ return path21.resolve(moduleDir, "..", "scripts", "faiss_index.py");
11846
12269
  }
11847
12270
  var FaissConversationIndexAdapter = class {
11848
12271
  constructor(config) {
11849
12272
  this.config = config;
11850
12273
  this.pythonBin = config.pythonBin && config.pythonBin.trim().length > 0 ? config.pythonBin.trim() : "python3";
11851
12274
  this.scriptPath = config.scriptPath && config.scriptPath.trim().length > 0 ? config.scriptPath.trim() : resolveDefaultFaissScriptPath();
11852
- this.indexPath = path20.isAbsolute(config.indexDir) ? config.indexDir : path20.join(config.memoryDir, config.indexDir);
12275
+ this.indexPath = path21.isAbsolute(config.indexDir) ? config.indexDir : path21.join(config.memoryDir, config.indexDir);
11853
12276
  this.spawnFn = config.spawnFn ?? childProcess.spawn;
11854
12277
  }
11855
12278
  pythonBin;
@@ -12030,7 +12453,7 @@ async function searchConversationIndexFaissFailOpen(adapter, query, maxResults)
12030
12453
  }
12031
12454
 
12032
12455
  // src/namespaces/storage.ts
12033
- import path21 from "path";
12456
+ import path22 from "path";
12034
12457
  import { access } from "fs/promises";
12035
12458
  async function exists(p) {
12036
12459
  try {
@@ -12052,7 +12475,7 @@ var NamespaceStorageRouter = class {
12052
12475
  this.defaultNsRootResolved = this.config.memoryDir;
12053
12476
  return this.defaultNsRootResolved;
12054
12477
  }
12055
- const nsDir = path21.join(this.config.memoryDir, "namespaces", this.config.defaultNamespace);
12478
+ const nsDir = path22.join(this.config.memoryDir, "namespaces", this.config.defaultNamespace);
12056
12479
  this.defaultNsRootResolved = await exists(nsDir) ? nsDir : this.config.memoryDir;
12057
12480
  return this.defaultNsRootResolved;
12058
12481
  }
@@ -12061,7 +12484,7 @@ var NamespaceStorageRouter = class {
12061
12484
  if (namespace === this.config.defaultNamespace) {
12062
12485
  return this.defaultNsRootResolved ?? this.config.memoryDir;
12063
12486
  }
12064
- return path21.join(this.config.memoryDir, "namespaces", namespace);
12487
+ return path22.join(this.config.memoryDir, "namespaces", namespace);
12065
12488
  }
12066
12489
  async storageFor(namespace) {
12067
12490
  const ns = namespace || this.config.defaultNamespace;
@@ -12137,8 +12560,8 @@ function recallNamespacesForPrincipal(principal, config) {
12137
12560
  }
12138
12561
 
12139
12562
  // src/shared-context/manager.ts
12140
- import { mkdir as mkdir15, readFile as readFile15, readdir as readdir9, appendFile as appendFile5, writeFile as writeFile14, stat as stat4 } from "fs/promises";
12141
- import path22 from "path";
12563
+ import { mkdir as mkdir15, readFile as readFile16, readdir as readdir10, appendFile as appendFile5, writeFile as writeFile15, stat as stat4 } from "fs/promises";
12564
+ import path23 from "path";
12142
12565
  import os3 from "os";
12143
12566
  import { z as z3 } from "zod";
12144
12567
  var SharedFeedbackEntrySchema = z3.object({
@@ -12334,15 +12757,15 @@ async function computeSemanticOverlapsWithTimeout(sources, timeoutMs, maxCandida
12334
12757
  var SharedContextManager = class {
12335
12758
  constructor(config) {
12336
12759
  this.config = config;
12337
- const base = typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0 ? config.sharedContextDir : path22.join(os3.homedir(), ".openclaw", "workspace", "shared-context");
12760
+ const base = typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0 ? config.sharedContextDir : path23.join(os3.homedir(), ".openclaw", "workspace", "shared-context");
12338
12761
  this.dir = base;
12339
- this.prioritiesPath = path22.join(base, "priorities.md");
12340
- this.prioritiesInboxPath = path22.join(base, "priorities.inbox.md");
12341
- this.outputsDir = path22.join(base, "agent-outputs");
12342
- this.roundtableDir = path22.join(base, "roundtable");
12343
- this.feedbackDir = path22.join(base, "feedback");
12344
- this.feedbackInboxPath = path22.join(this.feedbackDir, "inbox.jsonl");
12345
- this.crossSignalsDir = path22.join(base, "cross-signals");
12762
+ this.prioritiesPath = path23.join(base, "priorities.md");
12763
+ this.prioritiesInboxPath = path23.join(base, "priorities.inbox.md");
12764
+ this.outputsDir = path23.join(base, "agent-outputs");
12765
+ this.roundtableDir = path23.join(base, "roundtable");
12766
+ this.feedbackDir = path23.join(base, "feedback");
12767
+ this.feedbackInboxPath = path23.join(this.feedbackDir, "inbox.jsonl");
12768
+ this.crossSignalsDir = path23.join(base, "cross-signals");
12346
12769
  }
12347
12770
  dir;
12348
12771
  prioritiesPath;
@@ -12358,10 +12781,10 @@ var SharedContextManager = class {
12358
12781
  await mkdir15(this.roundtableDir, { recursive: true });
12359
12782
  await mkdir15(this.feedbackDir, { recursive: true });
12360
12783
  await mkdir15(this.crossSignalsDir, { recursive: true });
12361
- await mkdir15(path22.join(this.dir, "staging"), { recursive: true });
12362
- await mkdir15(path22.join(this.dir, "kpis"), { recursive: true });
12363
- await mkdir15(path22.join(this.dir, "calendar"), { recursive: true });
12364
- await mkdir15(path22.join(this.dir, "content-calendar"), { recursive: true });
12784
+ await mkdir15(path23.join(this.dir, "staging"), { recursive: true });
12785
+ await mkdir15(path23.join(this.dir, "kpis"), { recursive: true });
12786
+ await mkdir15(path23.join(this.dir, "calendar"), { recursive: true });
12787
+ await mkdir15(path23.join(this.dir, "content-calendar"), { recursive: true });
12365
12788
  await this.ensureFile(
12366
12789
  this.prioritiesPath,
12367
12790
  [
@@ -12392,22 +12815,22 @@ var SharedContextManager = class {
12392
12815
  try {
12393
12816
  await stat4(fp);
12394
12817
  } catch {
12395
- await writeFile14(fp, content, "utf-8");
12818
+ await writeFile15(fp, content, "utf-8");
12396
12819
  }
12397
12820
  }
12398
12821
  async readPriorities() {
12399
12822
  try {
12400
- return await readFile15(this.prioritiesPath, "utf-8");
12823
+ return await readFile16(this.prioritiesPath, "utf-8");
12401
12824
  } catch {
12402
12825
  return "";
12403
12826
  }
12404
12827
  }
12405
12828
  async readLatestRoundtable() {
12406
12829
  try {
12407
- const files = (await readdir9(this.roundtableDir)).filter((f) => f.endsWith(".md")).sort().reverse();
12408
- const fp = files[0] ? path22.join(this.roundtableDir, files[0]) : null;
12830
+ const files = (await readdir10(this.roundtableDir)).filter((f) => f.endsWith(".md")).sort().reverse();
12831
+ const fp = files[0] ? path23.join(this.roundtableDir, files[0]) : null;
12409
12832
  if (!fp) return "";
12410
- return await readFile15(fp, "utf-8");
12833
+ return await readFile16(fp, "utf-8");
12411
12834
  } catch {
12412
12835
  return "";
12413
12836
  }
@@ -12417,9 +12840,9 @@ var SharedContextManager = class {
12417
12840
  const date = ymd(createdAt);
12418
12841
  const time = createdAt.toISOString().slice(11, 19).replace(/:/g, "");
12419
12842
  const slug = safeSlug(opts.title);
12420
- const dir = path22.join(this.outputsDir, opts.agentId, date);
12843
+ const dir = path23.join(this.outputsDir, opts.agentId, date);
12421
12844
  await mkdir15(dir, { recursive: true });
12422
- const fp = path22.join(dir, `${time}-${slug}.md`);
12845
+ const fp = path23.join(dir, `${time}-${slug}.md`);
12423
12846
  const body = `---
12424
12847
  kind: agent_output
12425
12848
  agent: ${opts.agentId}
@@ -12428,7 +12851,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
12428
12851
  ---
12429
12852
 
12430
12853
  ` + opts.content.trimEnd() + "\n";
12431
- await writeFile14(fp, body, "utf-8");
12854
+ await writeFile15(fp, body, "utf-8");
12432
12855
  return fp;
12433
12856
  }
12434
12857
  async appendFeedback(entry) {
@@ -12451,15 +12874,15 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
12451
12874
  const maxChars = Math.max(2e3, opts.maxChars ?? 2e4);
12452
12875
  const outputs = [];
12453
12876
  try {
12454
- const agents = await readdir9(this.outputsDir, { withFileTypes: true });
12877
+ const agents = await readdir10(this.outputsDir, { withFileTypes: true });
12455
12878
  for (const a of agents) {
12456
12879
  if (!a.isDirectory()) continue;
12457
- const dayDir = path22.join(this.outputsDir, a.name, date);
12880
+ const dayDir = path23.join(this.outputsDir, a.name, date);
12458
12881
  try {
12459
- const files = (await readdir9(dayDir)).filter((f) => f.endsWith(".md")).sort();
12882
+ const files = (await readdir10(dayDir)).filter((f) => f.endsWith(".md")).sort();
12460
12883
  for (const f of files) {
12461
- const p = path22.join(dayDir, f);
12462
- const raw = await readFile15(p, "utf-8");
12884
+ const p = path23.join(dayDir, f);
12885
+ const raw = await readFile16(p, "utf-8");
12463
12886
  const title = (raw.match(/^title:\s*(.+)$/m)?.[1] ?? f).trim();
12464
12887
  outputs.push({ agent: a.name, path: p, title, raw });
12465
12888
  }
@@ -12470,7 +12893,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
12470
12893
  }
12471
12894
  const feedback = [];
12472
12895
  try {
12473
- const raw = await readFile15(this.feedbackInboxPath, "utf-8");
12896
+ const raw = await readFile16(this.feedbackInboxPath, "utf-8");
12474
12897
  for (const line of raw.split("\n")) {
12475
12898
  if (!line.trim()) continue;
12476
12899
  try {
@@ -12565,8 +12988,8 @@ ${body}`)
12565
12988
  addedOverlapCount: semanticAddedOverlapCount
12566
12989
  }
12567
12990
  };
12568
- const crossSignalsPath = path22.join(this.crossSignalsDir, `${date}.json`);
12569
- await writeFile14(crossSignalsPath, `${JSON.stringify(crossSignalReport, null, 2)}
12991
+ const crossSignalsPath = path23.join(this.crossSignalsDir, `${date}.json`);
12992
+ await writeFile15(crossSignalsPath, `${JSON.stringify(crossSignalReport, null, 2)}
12570
12993
  `, "utf-8");
12571
12994
  const overlapBullets = mergedOverlaps.length === 0 ? ["- No multi-agent topic overlap detected."] : mergedOverlaps.slice(0, 8).map((entry) => `- \`${entry.token}\` (${entry.agentCount} agents: ${entry.agents.join(", ")})`);
12572
12995
  const md = [
@@ -12589,8 +13012,8 @@ ${body}`)
12589
13012
  ];
12590
13013
  const out = md.join("\n");
12591
13014
  const trimmed = out.length > maxChars ? out.slice(0, maxChars) + "\n\n...(trimmed)\n" : out;
12592
- const roundtablePath = path22.join(this.roundtableDir, `${date}.md`);
12593
- await writeFile14(roundtablePath, trimmed, "utf-8");
13015
+ const roundtablePath = path23.join(this.roundtableDir, `${date}.md`);
13016
+ await writeFile15(roundtablePath, trimmed, "utf-8");
12594
13017
  log.info(`shared-context curated daily roundtable: ${roundtablePath}`);
12595
13018
  return {
12596
13019
  date,
@@ -12602,8 +13025,8 @@ ${body}`)
12602
13025
  };
12603
13026
 
12604
13027
  // src/compounding/engine.ts
12605
- import { mkdir as mkdir16, readFile as readFile16, readdir as readdir10, writeFile as writeFile15 } from "fs/promises";
12606
- import path23 from "path";
13028
+ import { mkdir as mkdir16, readFile as readFile17, readdir as readdir11, writeFile as writeFile16 } from "fs/promises";
13029
+ import path24 from "path";
12607
13030
  import os4 from "os";
12608
13031
  function defaultTierMigrationCycleBudget(config, trigger) {
12609
13032
  if (trigger === "extraction") {
@@ -12650,7 +13073,7 @@ function sharedContextDir(config) {
12650
13073
  if (typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0) {
12651
13074
  return config.sharedContextDir;
12652
13075
  }
12653
- return path23.join(os4.homedir(), ".openclaw", "workspace", "shared-context");
13076
+ return path24.join(os4.homedir(), ".openclaw", "workspace", "shared-context");
12654
13077
  }
12655
13078
  function cadenceStaleWindowMs(cadence) {
12656
13079
  switch (cadence) {
@@ -12669,16 +13092,16 @@ function cadenceStaleWindowMs(cadence) {
12669
13092
  var CompoundingEngine = class {
12670
13093
  constructor(config) {
12671
13094
  this.config = config;
12672
- this.weeklyDir = path23.join(config.memoryDir, "compounding", "weekly");
12673
- this.rubricsPath = path23.join(config.memoryDir, "compounding", "rubrics.md");
12674
- this.mistakesPath = path23.join(config.memoryDir, "compounding", "mistakes.json");
12675
- this.feedbackInboxPath = path23.join(sharedContextDir(config), "feedback", "inbox.jsonl");
12676
- this.identityAnchorPath = path23.join(config.memoryDir, "identity", "identity-anchor.md");
12677
- this.identityIncidentsDir = path23.join(config.memoryDir, "identity", "incidents");
12678
- this.identityAuditWeeklyDir = path23.join(config.memoryDir, "identity", "audits", "weekly");
12679
- this.identityAuditMonthlyDir = path23.join(config.memoryDir, "identity", "audits", "monthly");
12680
- this.identityImprovementLoopsPath = path23.join(config.memoryDir, "identity", "improvement-loops.md");
12681
- this.memoryActionEventsPath = path23.join(config.memoryDir, "state", "memory-actions.jsonl");
13095
+ this.weeklyDir = path24.join(config.memoryDir, "compounding", "weekly");
13096
+ this.rubricsPath = path24.join(config.memoryDir, "compounding", "rubrics.md");
13097
+ this.mistakesPath = path24.join(config.memoryDir, "compounding", "mistakes.json");
13098
+ this.feedbackInboxPath = path24.join(sharedContextDir(config), "feedback", "inbox.jsonl");
13099
+ this.identityAnchorPath = path24.join(config.memoryDir, "identity", "identity-anchor.md");
13100
+ this.identityIncidentsDir = path24.join(config.memoryDir, "identity", "incidents");
13101
+ this.identityAuditWeeklyDir = path24.join(config.memoryDir, "identity", "audits", "weekly");
13102
+ this.identityAuditMonthlyDir = path24.join(config.memoryDir, "identity", "audits", "monthly");
13103
+ this.identityImprovementLoopsPath = path24.join(config.memoryDir, "identity", "improvement-loops.md");
13104
+ this.memoryActionEventsPath = path24.join(config.memoryDir, "state", "memory-actions.jsonl");
12682
13105
  }
12683
13106
  weeklyDir;
12684
13107
  rubricsPath;
@@ -12692,8 +13115,8 @@ var CompoundingEngine = class {
12692
13115
  memoryActionEventsPath;
12693
13116
  async ensureDirs() {
12694
13117
  await mkdir16(this.weeklyDir, { recursive: true });
12695
- await mkdir16(path23.dirname(this.mistakesPath), { recursive: true });
12696
- await mkdir16(path23.dirname(this.rubricsPath), { recursive: true });
13118
+ await mkdir16(path24.dirname(this.mistakesPath), { recursive: true });
13119
+ await mkdir16(path24.dirname(this.rubricsPath), { recursive: true });
12697
13120
  }
12698
13121
  async synthesizeWeekly(opts) {
12699
13122
  await this.ensureDirs();
@@ -12704,12 +13127,12 @@ var CompoundingEngine = class {
12704
13127
  const promotionCandidates = this.config.compoundingSemanticEnabled ? this.derivePromotionCandidates(outcomeSummary) : [];
12705
13128
  const mistakes = this.buildMistakes(entries, actionPatterns);
12706
13129
  const continuity = this.config.continuityAuditEnabled ? await this.readContinuityAuditReferences(weekId) : { monthId: monthIdFromIsoWeek(weekId), weeklyPath: null, monthlyPath: null };
12707
- const reportPath = path23.join(this.weeklyDir, `${weekId}.md`);
13130
+ const reportPath = path24.join(this.weeklyDir, `${weekId}.md`);
12708
13131
  const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns, mistakes.details, continuity, outcomeSummary, promotionCandidates);
12709
- await writeFile15(reportPath, md, "utf-8");
13132
+ await writeFile16(reportPath, md, "utf-8");
12710
13133
  const rubrics = this.formatRubrics(entries, outcomeSummary);
12711
- await writeFile15(this.rubricsPath, rubrics, "utf-8");
12712
- await writeFile15(
13134
+ await writeFile16(this.rubricsPath, rubrics, "utf-8");
13135
+ await writeFile16(
12713
13136
  this.mistakesPath,
12714
13137
  JSON.stringify({ updatedAt: mistakes.updatedAt, patterns: mistakes.patterns }, null, 2) + "\n",
12715
13138
  "utf-8"
@@ -12790,13 +13213,13 @@ var CompoundingEngine = class {
12790
13213
  ];
12791
13214
  const dir = period === "weekly" ? this.identityAuditWeeklyDir : this.identityAuditMonthlyDir;
12792
13215
  await mkdir16(dir, { recursive: true });
12793
- const reportPath = path23.join(dir, `${key}.md`);
12794
- await writeFile15(reportPath, lines.join("\n"), "utf-8");
13216
+ const reportPath = path24.join(dir, `${key}.md`);
13217
+ await writeFile16(reportPath, lines.join("\n"), "utf-8");
12795
13218
  return { period, key, reportPath };
12796
13219
  }
12797
13220
  async readMistakes() {
12798
13221
  try {
12799
- const raw = await readFile16(this.mistakesPath, "utf-8");
13222
+ const raw = await readFile17(this.mistakesPath, "utf-8");
12800
13223
  const parsed = JSON.parse(raw);
12801
13224
  if (!parsed || !Array.isArray(parsed.patterns)) return null;
12802
13225
  return parsed;
@@ -12810,7 +13233,7 @@ var CompoundingEngine = class {
12810
13233
  async readFeedbackEntriesForWeek(weekId) {
12811
13234
  const out = [];
12812
13235
  try {
12813
- const raw = await readFile16(this.feedbackInboxPath, "utf-8");
13236
+ const raw = await readFile17(this.feedbackInboxPath, "utf-8");
12814
13237
  const lines = raw.split("\n");
12815
13238
  for (let idx = 0; idx < lines.length; idx += 1) {
12816
13239
  const line = lines[idx];
@@ -12852,7 +13275,7 @@ var CompoundingEngine = class {
12852
13275
  async readActionEventsForWeek(weekId) {
12853
13276
  const out = [];
12854
13277
  try {
12855
- const raw = await readFile16(this.memoryActionEventsPath, "utf-8");
13278
+ const raw = await readFile17(this.memoryActionEventsPath, "utf-8");
12856
13279
  const lines = raw.split("\n");
12857
13280
  for (let idx = 0; idx < lines.length; idx += 1) {
12858
13281
  const line = lines[idx];
@@ -12890,7 +13313,7 @@ var CompoundingEngine = class {
12890
13313
  else if (event.outcome === "skipped") acc.counts.skipped += 1;
12891
13314
  else if (event.outcome === "failed") acc.counts.failed += 1;
12892
13315
  else acc.counts.unknown += 1;
12893
- acc.provenance.add(`${path23.basename(this.memoryActionEventsPath)}:L${event.line}`);
13316
+ acc.provenance.add(`${path24.basename(this.memoryActionEventsPath)}:L${event.line}`);
12894
13317
  byAction.set(key, acc);
12895
13318
  }
12896
13319
  const out = [];
@@ -12928,7 +13351,7 @@ var CompoundingEngine = class {
12928
13351
  const patterns = [];
12929
13352
  for (const wrapped of entries) {
12930
13353
  const e = wrapped.entry;
12931
- const provenance = [`${path23.basename(wrapped.sourcePath)}:L${wrapped.sourceLine}#${wrapped.entryId}`];
13354
+ const provenance = [`${path24.basename(wrapped.sourcePath)}:L${wrapped.sourceLine}#${wrapped.entryId}`];
12932
13355
  if (e.learning && e.learning.trim().length > 0) {
12933
13356
  patterns.push({ pattern: `${e.agent}: ${e.learning.trim()}`, provenance });
12934
13357
  continue;
@@ -12938,7 +13361,7 @@ var CompoundingEngine = class {
12938
13361
  }
12939
13362
  }
12940
13363
  for (const p of actionPatterns) {
12941
- patterns.push({ pattern: p, provenance: [`${path23.basename(this.memoryActionEventsPath)}:*`] });
13364
+ patterns.push({ pattern: p, provenance: [`${path24.basename(this.memoryActionEventsPath)}:*`] });
12942
13365
  }
12943
13366
  const byPattern = /* @__PURE__ */ new Map();
12944
13367
  for (const p of patterns) {
@@ -12978,7 +13401,7 @@ var CompoundingEngine = class {
12978
13401
  lines.push(`- approved: ${approved}`);
12979
13402
  lines.push(`- approved_with_feedback: ${awf}`);
12980
13403
  lines.push(`- rejected: ${rejected}`);
12981
- const provenance = list.slice(0, 3).map((e) => `${path23.basename(e.sourcePath)}:L${e.sourceLine}#${e.entryId}`);
13404
+ const provenance = list.slice(0, 3).map((e) => `${path24.basename(e.sourcePath)}:L${e.sourceLine}#${e.entryId}`);
12982
13405
  if (provenance.length > 0) {
12983
13406
  lines.push(`- provenance: ${provenance.join(", ")}`);
12984
13407
  }
@@ -13069,7 +13492,7 @@ var CompoundingEngine = class {
13069
13492
  } else {
13070
13493
  for (const item of learnings) {
13071
13494
  const note = (item.entry.learning && item.entry.learning.trim().length > 0 ? item.entry.learning : item.entry.reason).trim();
13072
- lines.push(`- ${note} _(source: ${path23.basename(item.sourcePath)}:L${item.sourceLine}#${item.entryId})_`);
13495
+ lines.push(`- ${note} _(source: ${path24.basename(item.sourcePath)}:L${item.sourceLine}#${item.entryId})_`);
13073
13496
  }
13074
13497
  }
13075
13498
  lines.push("");
@@ -13090,7 +13513,7 @@ var CompoundingEngine = class {
13090
13513
  }
13091
13514
  async readNonEmptyFile(filePath) {
13092
13515
  try {
13093
- const raw = await readFile16(filePath, "utf-8");
13516
+ const raw = await readFile17(filePath, "utf-8");
13094
13517
  return raw.trim().length > 0;
13095
13518
  } catch {
13096
13519
  return false;
@@ -13098,7 +13521,7 @@ var CompoundingEngine = class {
13098
13521
  }
13099
13522
  async readOptionalFile(filePath) {
13100
13523
  try {
13101
- const raw = await readFile16(filePath, "utf-8");
13524
+ const raw = await readFile17(filePath, "utf-8");
13102
13525
  return raw.trim().length > 0 ? raw : null;
13103
13526
  } catch {
13104
13527
  return null;
@@ -13110,13 +13533,13 @@ var CompoundingEngine = class {
13110
13533
  if (cappedLimit === 0) return [];
13111
13534
  const incidents = [];
13112
13535
  try {
13113
- const names = await readdir10(this.identityIncidentsDir);
13536
+ const names = await readdir11(this.identityIncidentsDir);
13114
13537
  const files = names.filter((n) => n.endsWith(".md")).sort().reverse();
13115
13538
  for (const file of files) {
13116
13539
  if (incidents.length >= cappedLimit) break;
13117
- const filePath = path23.join(this.identityIncidentsDir, file);
13540
+ const filePath = path24.join(this.identityIncidentsDir, file);
13118
13541
  try {
13119
- const raw = await readFile16(filePath, "utf-8");
13542
+ const raw = await readFile17(filePath, "utf-8");
13120
13543
  const parsed = parseContinuityIncident(raw);
13121
13544
  if (!parsed) continue;
13122
13545
  if (state && parsed.state !== state) continue;
@@ -13130,8 +13553,8 @@ var CompoundingEngine = class {
13130
13553
  }
13131
13554
  async readContinuityAuditReferences(weekId) {
13132
13555
  const monthId = monthIdFromIsoWeek(weekId);
13133
- const weeklyPath = path23.join(this.identityAuditWeeklyDir, `${weekId}.md`);
13134
- const monthlyPath = path23.join(this.identityAuditMonthlyDir, `${monthId}.md`);
13556
+ const weeklyPath = path24.join(this.identityAuditWeeklyDir, `${weekId}.md`);
13557
+ const monthlyPath = path24.join(this.identityAuditMonthlyDir, `${monthId}.md`);
13135
13558
  const weeklyExists = await this.readNonEmptyFile(weeklyPath);
13136
13559
  const monthlyExists = await this.readNonEmptyFile(monthlyPath);
13137
13560
  return {
@@ -13145,7 +13568,7 @@ var CompoundingEngine = class {
13145
13568
 
13146
13569
  // src/tier-migration.ts
13147
13570
  import { appendFile as appendFile6, mkdir as mkdir17 } from "fs/promises";
13148
- import path24 from "path";
13571
+ import path25 from "path";
13149
13572
  var TierMigrationExecutor = class {
13150
13573
  storage;
13151
13574
  qmd;
@@ -13159,7 +13582,7 @@ var TierMigrationExecutor = class {
13159
13582
  this.hotCollection = options.hotCollection;
13160
13583
  this.coldCollection = options.coldCollection;
13161
13584
  this.autoEmbed = options.autoEmbed === true;
13162
- this.journalPath = options.journalPath ?? path24.join(this.storage.dir, "state", "tier-migration-journal.jsonl");
13585
+ this.journalPath = options.journalPath ?? path25.join(this.storage.dir, "state", "tier-migration-journal.jsonl");
13163
13586
  }
13164
13587
  async migrateMemory(request) {
13165
13588
  const { memory, fromTier, toTier, reason } = request;
@@ -13212,7 +13635,7 @@ var TierMigrationExecutor = class {
13212
13635
  reason: result.reason,
13213
13636
  targetPath: result.targetPath
13214
13637
  };
13215
- await mkdir17(path24.dirname(this.journalPath), { recursive: true });
13638
+ await mkdir17(path25.dirname(this.journalPath), { recursive: true });
13216
13639
  await appendFile6(this.journalPath, `${JSON.stringify(entry)}
13217
13640
  `, "utf-8");
13218
13641
  }
@@ -13378,8 +13801,8 @@ function selectRouteRule(text, rules, options) {
13378
13801
  }
13379
13802
 
13380
13803
  // src/routing/store.ts
13381
- import { lstat, mkdir as mkdir18, readFile as readFile17, realpath, rename as rename2, rm as rm2, stat as stat5, writeFile as writeFile16 } from "fs/promises";
13382
- import path25 from "path";
13804
+ import { lstat, mkdir as mkdir18, readFile as readFile18, realpath, rename as rename2, rm as rm2, stat as stat5, writeFile as writeFile17 } from "fs/promises";
13805
+ import path26 from "path";
13383
13806
  import { createHash as createHash4 } from "crypto";
13384
13807
  function defaultState() {
13385
13808
  return {
@@ -13398,14 +13821,14 @@ function stableRuleId(rule) {
13398
13821
  return `route-${createHash4("sha256").update(seed).digest("hex").slice(0, 12)}`;
13399
13822
  }
13400
13823
  function resolveStatePath(memoryDir, stateFile) {
13401
- const root = path25.resolve(memoryDir);
13402
- const defaultPath = path25.join(root, "state", "routing-rules.json");
13403
- if (path25.isAbsolute(stateFile)) {
13404
- const absolute = path25.resolve(stateFile);
13405
- return absolute.startsWith(root + path25.sep) ? absolute : defaultPath;
13824
+ const root = path26.resolve(memoryDir);
13825
+ const defaultPath = path26.join(root, "state", "routing-rules.json");
13826
+ if (path26.isAbsolute(stateFile)) {
13827
+ const absolute = path26.resolve(stateFile);
13828
+ return absolute.startsWith(root + path26.sep) ? absolute : defaultPath;
13406
13829
  }
13407
- const resolved = path25.resolve(root, stateFile);
13408
- return resolved.startsWith(root + path25.sep) ? resolved : defaultPath;
13830
+ const resolved = path26.resolve(root, stateFile);
13831
+ return resolved.startsWith(root + path26.sep) ? resolved : defaultPath;
13409
13832
  }
13410
13833
  function normalizeRule(rule, options) {
13411
13834
  if (!rule || typeof rule !== "object") return null;
@@ -13438,7 +13861,7 @@ var RoutingRulesStore = class {
13438
13861
  lockPath;
13439
13862
  writeQueue = Promise.resolve();
13440
13863
  constructor(memoryDir, stateFile = "state/routing-rules.json") {
13441
- this.memoryRoot = path25.resolve(memoryDir);
13864
+ this.memoryRoot = path26.resolve(memoryDir);
13442
13865
  this.statePath = resolveStatePath(memoryDir, stateFile);
13443
13866
  this.lockPath = `${this.statePath}.lock`;
13444
13867
  }
@@ -13476,7 +13899,7 @@ var RoutingRulesStore = class {
13476
13899
  await this.withWriteLock(async () => {
13477
13900
  const payload = defaultState();
13478
13901
  await this.assertStatePathScoped();
13479
- await writeFile16(this.statePath, JSON.stringify(payload, null, 2), "utf-8");
13902
+ await writeFile17(this.statePath, JSON.stringify(payload, null, 2), "utf-8");
13480
13903
  });
13481
13904
  }
13482
13905
  dedupeById(rules) {
@@ -13489,7 +13912,7 @@ var RoutingRulesStore = class {
13489
13912
  async readPersistedRules() {
13490
13913
  try {
13491
13914
  await this.assertStatePathScoped();
13492
- const raw = await readFile17(this.statePath, "utf-8");
13915
+ const raw = await readFile18(this.statePath, "utf-8");
13493
13916
  const parsed = JSON.parse(raw);
13494
13917
  if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.rules)) return [];
13495
13918
  const normalized = parsed.rules.map((rule) => normalizeRule(rule)).filter((rule) => rule !== null);
@@ -13510,7 +13933,7 @@ var RoutingRulesStore = class {
13510
13933
  const tmpPath = `${this.statePath}.tmp-${process.pid}-${Date.now()}`;
13511
13934
  try {
13512
13935
  await this.assertStatePathScoped();
13513
- await writeFile16(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
13936
+ await writeFile17(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
13514
13937
  await rename2(tmpPath, this.statePath);
13515
13938
  } catch (err) {
13516
13939
  log.debug(`routing rules write failed: ${err}`);
@@ -13544,7 +13967,7 @@ var RoutingRulesStore = class {
13544
13967
  const timeoutMs = 5e3;
13545
13968
  let unexpectedLockError = null;
13546
13969
  await this.assertStatePathScoped();
13547
- await mkdir18(path25.dirname(this.lockPath), { recursive: true });
13970
+ await mkdir18(path26.dirname(this.lockPath), { recursive: true });
13548
13971
  while (Date.now() - start < timeoutMs) {
13549
13972
  try {
13550
13973
  await mkdir18(this.lockPath);
@@ -13579,12 +14002,12 @@ var RoutingRulesStore = class {
13579
14002
  async assertStatePathScoped() {
13580
14003
  await mkdir18(this.memoryRoot, { recursive: true });
13581
14004
  const canonicalRoot = await realpath(this.memoryRoot);
13582
- const canonicalParent = await this.canonicalizePathWithoutCreating(path25.dirname(this.statePath));
13583
- const canonicalStatePath = path25.join(canonicalParent, path25.basename(this.statePath));
14005
+ const canonicalParent = await this.canonicalizePathWithoutCreating(path26.dirname(this.statePath));
14006
+ const canonicalStatePath = path26.join(canonicalParent, path26.basename(this.statePath));
13584
14007
  if (!this.isPathInside(canonicalRoot, canonicalStatePath)) {
13585
14008
  throw new Error(`routing rules state path escaped memoryDir: ${canonicalStatePath}`);
13586
14009
  }
13587
- await mkdir18(path25.dirname(this.statePath), { recursive: true });
14010
+ await mkdir18(path26.dirname(this.statePath), { recursive: true });
13588
14011
  try {
13589
14012
  const stateStats = await lstat(this.statePath);
13590
14013
  if (stateStats.isSymbolicLink()) {
@@ -13601,28 +14024,28 @@ var RoutingRulesStore = class {
13601
14024
  }
13602
14025
  }
13603
14026
  isPathInside(root, candidate) {
13604
- const normalizedRoot = path25.resolve(root);
13605
- const normalizedCandidate = path25.resolve(candidate);
14027
+ const normalizedRoot = path26.resolve(root);
14028
+ const normalizedCandidate = path26.resolve(candidate);
13606
14029
  if (normalizedCandidate === normalizedRoot) return true;
13607
- if (normalizedRoot === path25.parse(normalizedRoot).root) {
14030
+ if (normalizedRoot === path26.parse(normalizedRoot).root) {
13608
14031
  return normalizedCandidate.startsWith(normalizedRoot);
13609
14032
  }
13610
- return normalizedCandidate.startsWith(`${normalizedRoot}${path25.sep}`);
14033
+ return normalizedCandidate.startsWith(`${normalizedRoot}${path26.sep}`);
13611
14034
  }
13612
14035
  async canonicalizePathWithoutCreating(targetPath) {
13613
- const absoluteTarget = path25.resolve(targetPath);
14036
+ const absoluteTarget = path26.resolve(targetPath);
13614
14037
  let probe = absoluteTarget;
13615
14038
  while (true) {
13616
14039
  try {
13617
14040
  const canonicalProbe = await realpath(probe);
13618
- const remainder = path25.relative(probe, absoluteTarget);
13619
- return path25.resolve(canonicalProbe, remainder);
14041
+ const remainder = path26.relative(probe, absoluteTarget);
14042
+ return path26.resolve(canonicalProbe, remainder);
13620
14043
  } catch (err) {
13621
14044
  const code = err.code;
13622
14045
  if (code !== "ENOENT") {
13623
14046
  throw err;
13624
14047
  }
13625
- const parent = path25.dirname(probe);
14048
+ const parent = path26.dirname(probe);
13626
14049
  if (parent === probe) {
13627
14050
  return absoluteTarget;
13628
14051
  }
@@ -13633,8 +14056,8 @@ var RoutingRulesStore = class {
13633
14056
  };
13634
14057
 
13635
14058
  // src/policy-runtime.ts
13636
- import path26 from "path";
13637
- import { mkdir as mkdir19, readFile as readFile18, rename as rename3, writeFile as writeFile17 } from "fs/promises";
14059
+ import path27 from "path";
14060
+ import { mkdir as mkdir19, readFile as readFile19, rename as rename3, writeFile as writeFile18 } from "fs/promises";
13638
14061
  var RUNTIME_POLICY_VERSION = 1;
13639
14062
  var RUNTIME_POLICY_FILE = "policy-runtime.json";
13640
14063
  var RUNTIME_POLICY_PREV_FILE = "policy-runtime.prev.json";
@@ -13663,7 +14086,7 @@ function isRuntimeParameter(parameter) {
13663
14086
  }
13664
14087
  async function readRuntimePolicySnapshot(filePath, options) {
13665
14088
  try {
13666
- const raw = await readFile18(filePath, "utf-8");
14089
+ const raw = await readFile19(filePath, "utf-8");
13667
14090
  const parsed = JSON.parse(raw);
13668
14091
  if (!parsed || typeof parsed.version !== "number" || parsed.version < 1 || typeof parsed.updatedAt !== "string" || !parsed.values || typeof parsed.values !== "object" || typeof parsed.sourceAdjustmentCount !== "number" || parsed.sourceAdjustmentCount < 0) {
13669
14092
  return null;
@@ -13682,8 +14105,8 @@ async function readRuntimePolicySnapshot(filePath, options) {
13682
14105
  }
13683
14106
  async function writeSnapshotAtomic(filePath, snapshot) {
13684
14107
  const tempPath = `${filePath}.tmp`;
13685
- await mkdir19(path26.dirname(filePath), { recursive: true });
13686
- await writeFile17(tempPath, `${JSON.stringify(snapshot, null, 2)}
14108
+ await mkdir19(path27.dirname(filePath), { recursive: true });
14109
+ await writeFile18(tempPath, `${JSON.stringify(snapshot, null, 2)}
13687
14110
  `, "utf-8");
13688
14111
  await rename3(tempPath, filePath);
13689
14112
  }
@@ -13691,9 +14114,9 @@ var PolicyRuntimeManager = class {
13691
14114
  constructor(memoryDir, config) {
13692
14115
  this.memoryDir = memoryDir;
13693
14116
  this.config = config;
13694
- const stateDir2 = path26.join(memoryDir, "state");
13695
- this.runtimePath = path26.join(stateDir2, RUNTIME_POLICY_FILE);
13696
- this.runtimePrevPath = path26.join(stateDir2, RUNTIME_POLICY_PREV_FILE);
14117
+ const stateDir2 = path27.join(memoryDir, "state");
14118
+ this.runtimePath = path27.join(stateDir2, RUNTIME_POLICY_FILE);
14119
+ this.runtimePrevPath = path27.join(stateDir2, RUNTIME_POLICY_PREV_FILE);
13697
14120
  }
13698
14121
  runtimePath;
13699
14122
  runtimePrevPath;
@@ -13977,11 +14400,11 @@ function mergeGraphExpandedResults(primary, expanded) {
13977
14400
  return Array.from(mergedByPath.values());
13978
14401
  }
13979
14402
  function graphPathRelativeToStorage(storageDir, candidatePath) {
13980
- const absolutePath = path27.isAbsolute(candidatePath) ? candidatePath : path27.resolve(storageDir, candidatePath);
13981
- const rel = path27.relative(storageDir, absolutePath);
14403
+ const absolutePath = path28.isAbsolute(candidatePath) ? candidatePath : path28.resolve(storageDir, candidatePath);
14404
+ const rel = path28.relative(storageDir, absolutePath);
13982
14405
  if (!rel || rel === ".") return null;
13983
14406
  if (rel.startsWith("..")) return null;
13984
- return rel.split(path27.sep).join("/");
14407
+ return rel.split(path28.sep).join("/");
13985
14408
  }
13986
14409
  function normalizeGraphActivationScore(score) {
13987
14410
  const bounded = Number.isFinite(score) && score > 0 ? score : 0;
@@ -14056,7 +14479,7 @@ function buildMemoryPathById(allMemsForGraph, storageDir) {
14056
14479
  for (const mem of allMemsForGraph ?? []) {
14057
14480
  const id = mem.frontmatter.id;
14058
14481
  if (!id) continue;
14059
- pathById.set(id, path27.relative(storageDir, mem.path));
14482
+ pathById.set(id, path28.relative(storageDir, mem.path));
14060
14483
  }
14061
14484
  return pathById;
14062
14485
  }
@@ -14064,7 +14487,7 @@ function appendMemoryToGraphContext(options) {
14064
14487
  if (!Array.isArray(options.allMemsForGraph)) return;
14065
14488
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
14066
14489
  options.allMemsForGraph.push({
14067
- path: path27.join(options.storageDir, options.memoryRelPath),
14490
+ path: path28.join(options.storageDir, options.memoryRelPath),
14068
14491
  content: options.content,
14069
14492
  frontmatter: {
14070
14493
  id: options.memoryId,
@@ -14084,15 +14507,15 @@ function resolvePersistedMemoryRelativePath(options) {
14084
14507
  const persisted = options.pathById.get(options.memoryId);
14085
14508
  if (persisted) return persisted;
14086
14509
  if (options.category === "correction") {
14087
- return path27.join("corrections", `${options.memoryId}.md`);
14510
+ return path28.join("corrections", `${options.memoryId}.md`);
14088
14511
  }
14089
14512
  const idParts = options.memoryId.split("-");
14090
14513
  const maybeTimestamp = Number(idParts[1]);
14091
14514
  if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
14092
14515
  const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
14093
- return path27.join("facts", day, `${options.memoryId}.md`);
14516
+ return path28.join("facts", day, `${options.memoryId}.md`);
14094
14517
  }
14095
- return path27.join("facts", `${options.memoryId}.md`);
14518
+ return path28.join("facts", `${options.memoryId}.md`);
14096
14519
  }
14097
14520
  var Orchestrator = class _Orchestrator {
14098
14521
  storage;
@@ -14202,7 +14625,7 @@ var Orchestrator = class _Orchestrator {
14202
14625
  this.compounding = config.compoundingEnabled ? new CompoundingEngine(config) : void 0;
14203
14626
  this.buffer = new SmartBuffer(config, this.storage);
14204
14627
  this.transcript = new TranscriptManager(config);
14205
- this.conversationIndexDir = path27.join(config.memoryDir, "conversation-index", "chunks");
14628
+ this.conversationIndexDir = path28.join(config.memoryDir, "conversation-index", "chunks");
14206
14629
  this.modelRegistry = new ModelRegistry(config.memoryDir);
14207
14630
  this.relevance = new RelevanceStore(config.memoryDir);
14208
14631
  this.negatives = new NegativeExampleStore(config.memoryDir);
@@ -14219,7 +14642,7 @@ var Orchestrator = class _Orchestrator {
14219
14642
  this.localLlm = new LocalLlmClient(config, this.modelRegistry);
14220
14643
  this.extraction = new ExtractionEngine(config, this.localLlm, config.gatewayConfig, this.modelRegistry);
14221
14644
  this.threading = new ThreadingManager(
14222
- path27.join(config.memoryDir, "threads"),
14645
+ path28.join(config.memoryDir, "threads"),
14223
14646
  config.threadingGapMinutes
14224
14647
  );
14225
14648
  this.tmtBuilder = new TmtBuilder(config.memoryDir, {
@@ -14388,7 +14811,7 @@ var Orchestrator = class _Orchestrator {
14388
14811
  await this.sessionObserver.load();
14389
14812
  this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
14390
14813
  if (this.config.factDeduplicationEnabled) {
14391
- const stateDir2 = path27.join(this.config.memoryDir, "state");
14814
+ const stateDir2 = path28.join(this.config.memoryDir, "state");
14392
14815
  this.contentHashIndex = new ContentHashIndex(stateDir2);
14393
14816
  await this.contentHashIndex.load();
14394
14817
  log.info(`content-hash dedup: loaded ${this.contentHashIndex.size} hashes`);
@@ -14426,7 +14849,7 @@ var Orchestrator = class _Orchestrator {
14426
14849
  if (available) {
14427
14850
  log.info(`Conversation index QMD: available ${this.conversationQmd.debugStatus()}`);
14428
14851
  const collectionState = await this.conversationQmd.ensureCollection(
14429
- path27.join(this.config.memoryDir, "conversation-index")
14852
+ path28.join(this.config.memoryDir, "conversation-index")
14430
14853
  );
14431
14854
  if (collectionState === "missing") {
14432
14855
  this.config.conversationIndexEnabled = false;
@@ -14480,12 +14903,12 @@ var Orchestrator = class _Orchestrator {
14480
14903
  this.lastFileHygieneRunAtMs = now;
14481
14904
  if (hygiene.rotateEnabled) {
14482
14905
  for (const rel of hygiene.rotatePaths) {
14483
- const abs = path27.isAbsolute(rel) ? rel : path27.join(this.config.workspaceDir, rel);
14906
+ const abs = path28.isAbsolute(rel) ? rel : path28.join(this.config.workspaceDir, rel);
14484
14907
  try {
14485
- const raw = await readFile19(abs, "utf-8");
14908
+ const raw = await readFile20(abs, "utf-8");
14486
14909
  if (raw.length > hygiene.rotateMaxBytes) {
14487
- const archiveDir = path27.join(this.config.workspaceDir, hygiene.archiveDir);
14488
- const base = path27.basename(abs);
14910
+ const archiveDir = path28.join(this.config.workspaceDir, hygiene.archiveDir);
14911
+ const base = path28.basename(abs);
14489
14912
  const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
14490
14913
  const { newContent } = await rotateMarkdownFileToArchive({
14491
14914
  filePath: abs,
@@ -14493,7 +14916,7 @@ var Orchestrator = class _Orchestrator {
14493
14916
  archivePrefix: prefix,
14494
14917
  keepTailChars: hygiene.rotateKeepTailChars
14495
14918
  });
14496
- await writeFile18(abs, newContent, "utf-8");
14919
+ await writeFile19(abs, newContent, "utf-8");
14497
14920
  }
14498
14921
  } catch {
14499
14922
  }
@@ -14510,8 +14933,8 @@ var Orchestrator = class _Orchestrator {
14510
14933
  log.warn(w.message);
14511
14934
  }
14512
14935
  if (hygiene.warningsLogEnabled && warnings.length > 0) {
14513
- const fp = path27.join(this.config.memoryDir, hygiene.warningsLogPath);
14514
- await mkdir20(path27.dirname(fp), { recursive: true });
14936
+ const fp = path28.join(this.config.memoryDir, hygiene.warningsLogPath);
14937
+ await mkdir20(path28.dirname(fp), { recursive: true });
14515
14938
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
14516
14939
  const block = `
14517
14940
 
@@ -14520,11 +14943,11 @@ var Orchestrator = class _Orchestrator {
14520
14943
  ` + warnings.map((w) => `- ${w.message}`).join("\n") + "\n";
14521
14944
  let existing = "";
14522
14945
  try {
14523
- existing = await readFile19(fp, "utf-8");
14946
+ existing = await readFile20(fp, "utf-8");
14524
14947
  } catch {
14525
14948
  existing = "# Engram File Hygiene Warnings\n";
14526
14949
  }
14527
- await writeFile18(fp, existing + block, "utf-8");
14950
+ await writeFile19(fp, existing + block, "utf-8");
14528
14951
  }
14529
14952
  }
14530
14953
  }
@@ -14600,9 +15023,9 @@ var Orchestrator = class _Orchestrator {
14600
15023
  }
14601
15024
  async getLastGraphRecallSnapshot(namespace) {
14602
15025
  const storage = await this.getStorage(namespace);
14603
- const snapshotPath = path27.join(storage.dir, "state", "last_graph_recall.json");
15026
+ const snapshotPath = path28.join(storage.dir, "state", "last_graph_recall.json");
14604
15027
  try {
14605
- const raw = await readFile19(snapshotPath, "utf-8");
15028
+ const raw = await readFile20(snapshotPath, "utf-8");
14606
15029
  const parsed = JSON.parse(raw);
14607
15030
  if (!parsed || typeof parsed !== "object") return null;
14608
15031
  return {
@@ -14668,10 +15091,10 @@ ${r.snippet.trim()}
14668
15091
  }
14669
15092
  async countConversationChunkDocs(dir) {
14670
15093
  try {
14671
- const entries = await readdir11(dir, { withFileTypes: true });
15094
+ const entries = await readdir12(dir, { withFileTypes: true });
14672
15095
  let total = 0;
14673
15096
  for (const entry of entries) {
14674
- const fullPath = path27.join(dir, entry.name);
15097
+ const fullPath = path28.join(dir, entry.name);
14675
15098
  if (entry.isDirectory()) {
14676
15099
  total += await this.countConversationChunkDocs(fullPath);
14677
15100
  continue;
@@ -14719,6 +15142,9 @@ ${r.snippet.trim()}
14719
15142
  qmdAvailable
14720
15143
  };
14721
15144
  }
15145
+ async getRecoverySummary(sessionKey) {
15146
+ return this.transcript.getRecoverySummary(sessionKey);
15147
+ }
14722
15148
  async updateConversationIndex(sessionKey, hours = 24, opts) {
14723
15149
  if (!this.config.conversationIndexEnabled) {
14724
15150
  return { chunks: 0, skipped: true, reason: "disabled", embedded: false };
@@ -14987,7 +15413,7 @@ ${r.snippet.trim()}
14987
15413
  const seedRelativePaths = seedCandidates.map((result) => graphPathRelativeToStorage(storage.dir, result.path)).filter((value) => typeof value === "string" && value.length > 0);
14988
15414
  if (seedRelativePaths.length === 0) continue;
14989
15415
  const seedRecallScore = seedCandidates.reduce((max, item) => Math.max(max, item.score), 0);
14990
- seedPaths.push(...seedRelativePaths.map((rel) => path27.join(storage.dir, rel)));
15416
+ seedPaths.push(...seedRelativePaths.map((rel) => path28.join(storage.dir, rel)));
14991
15417
  const seedSet = new Set(seedRelativePaths);
14992
15418
  const expanded = await this.graphIndexFor(storage).spreadingActivation(
14993
15419
  seedRelativePaths,
@@ -14996,7 +15422,7 @@ ${r.snippet.trim()}
14996
15422
  if (expanded.length === 0) continue;
14997
15423
  for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
14998
15424
  if (seedSet.has(candidate.path)) continue;
14999
- const memoryPath = path27.resolve(storage.dir, candidate.path);
15425
+ const memoryPath = path28.resolve(storage.dir, candidate.path);
15000
15426
  const memory = await storage.readMemoryByPath(memoryPath);
15001
15427
  if (!memory) continue;
15002
15428
  if (isArtifactMemoryPath(memory.path)) continue;
@@ -15019,7 +15445,7 @@ ${r.snippet.trim()}
15019
15445
  path: memory.path,
15020
15446
  score,
15021
15447
  namespace,
15022
- seed: path27.resolve(storage.dir, candidate.seed),
15448
+ seed: path28.resolve(storage.dir, candidate.seed),
15023
15449
  hopDepth: candidate.hopDepth,
15024
15450
  decayedWeight: candidate.decayedWeight,
15025
15451
  graphType: candidate.graphType
@@ -15034,8 +15460,8 @@ ${r.snippet.trim()}
15034
15460
  }
15035
15461
  async recordLastGraphRecallSnapshot(options) {
15036
15462
  try {
15037
- const snapshotPath = path27.join(options.storage.dir, "state", "last_graph_recall.json");
15038
- await mkdir20(path27.dirname(snapshotPath), { recursive: true });
15463
+ const snapshotPath = path28.join(options.storage.dir, "state", "last_graph_recall.json");
15464
+ await mkdir20(path28.dirname(snapshotPath), { recursive: true });
15039
15465
  const now = (/* @__PURE__ */ new Date()).toISOString();
15040
15466
  const totalSeedCount = options.seedPaths.length;
15041
15467
  const totalExpandedCount = options.expandedPaths.length;
@@ -15052,7 +15478,7 @@ ${r.snippet.trim()}
15052
15478
  seeds,
15053
15479
  expanded
15054
15480
  };
15055
- await writeFile18(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
15481
+ await writeFile19(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
15056
15482
  } catch (err) {
15057
15483
  log.debug(`last graph recall write failed: ${err}`);
15058
15484
  }
@@ -16063,7 +16489,7 @@ _Context: ${topQuestion.context}_`);
16063
16489
  };
16064
16490
  this.tierMigrationInFlight = true;
16065
16491
  try {
16066
- const coldStorage = new StorageManager(path27.join(storage.dir, "cold"));
16492
+ const coldStorage = new StorageManager(path28.join(storage.dir, "cold"));
16067
16493
  const [hotMemories, coldMemories] = await Promise.all([
16068
16494
  storage.readAllMemories(),
16069
16495
  coldStorage.readAllMemories()
@@ -16657,7 +17083,7 @@ _Context: ${topQuestion.context}_`);
16657
17083
  const allMems = allMemsForGraph ?? [];
16658
17084
  for (const m of allMems) {
16659
17085
  if (m.frontmatter.entityRef === entityRef) {
16660
- const rel = path27.relative(storage.dir, m.path);
17086
+ const rel = path28.relative(storage.dir, m.path);
16661
17087
  if (rel !== memoryRelPath) entitySiblings.push(rel);
16662
17088
  }
16663
17089
  }
@@ -17215,9 +17641,9 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
17215
17641
  protectedCategories: this.config.lifecycleProtectedCategories
17216
17642
  }
17217
17643
  };
17218
- const metricsPath = path27.join(this.storage.dir, "state", "lifecycle-metrics.json");
17219
- await mkdir20(path27.dirname(metricsPath), { recursive: true });
17220
- await writeFile18(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
17644
+ const metricsPath = path28.join(this.storage.dir, "state", "lifecycle-metrics.json");
17645
+ await mkdir20(path28.dirname(metricsPath), { recursive: true });
17646
+ await writeFile19(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
17221
17647
  }
17222
17648
  /**
17223
17649
  * Archive old, low-importance, rarely-accessed facts (v6.0).
@@ -17484,7 +17910,7 @@ ${lines.join("\n\n")}`;
17484
17910
  if (hits.length === 0) return [];
17485
17911
  const results = [];
17486
17912
  for (const hit of hits) {
17487
- const fullPath = path27.isAbsolute(hit.path) ? hit.path : path27.join(this.config.memoryDir, hit.path);
17913
+ const fullPath = path28.isAbsolute(hit.path) ? hit.path : path28.join(this.config.memoryDir, hit.path);
17488
17914
  const memory = await this.storage.readMemoryByPath(fullPath);
17489
17915
  if (!memory) continue;
17490
17916
  results.push({
@@ -17942,8 +18368,8 @@ ${lines.join("\n\n")}`;
17942
18368
  }
17943
18369
  namespaceFromStorageDir(storageDir) {
17944
18370
  if (!this.config.namespacesEnabled) return this.config.defaultNamespace;
17945
- const resolvedStorageDir = path27.resolve(storageDir);
17946
- const resolvedMemoryDir = path27.resolve(this.config.memoryDir);
18371
+ const resolvedStorageDir = path28.resolve(storageDir);
18372
+ const resolvedMemoryDir = path28.resolve(this.config.memoryDir);
17947
18373
  if (resolvedStorageDir === resolvedMemoryDir) return this.config.defaultNamespace;
17948
18374
  const m = resolvedStorageDir.match(/[\\/]namespaces[\\/]([^\\/]+)$/);
17949
18375
  return m && m[1] ? m[1] : this.config.defaultNamespace;
@@ -17971,14 +18397,14 @@ ${lines.join("\n\n")}`;
17971
18397
  };
17972
18398
 
17973
18399
  // src/tools.ts
17974
- import path29 from "path";
18400
+ import path30 from "path";
17975
18401
  import { createHash as createHash7 } from "crypto";
17976
18402
  import { Type } from "@sinclair/typebox";
17977
18403
 
17978
18404
  // src/work/storage.ts
17979
- import path28 from "path";
18405
+ import path29 from "path";
17980
18406
  import { randomUUID } from "crypto";
17981
- import { mkdir as mkdir21, readdir as readdir12, readFile as readFile20, rm as rm3, writeFile as writeFile19 } from "fs/promises";
18407
+ import { mkdir as mkdir21, readdir as readdir13, readFile as readFile21, rm as rm3, writeFile as writeFile20 } from "fs/promises";
17982
18408
  var TASK_TRANSITIONS = {
17983
18409
  todo: /* @__PURE__ */ new Set(["in_progress", "blocked", "cancelled"]),
17984
18410
  in_progress: /* @__PURE__ */ new Set(["todo", "blocked", "done", "cancelled"]),
@@ -18053,8 +18479,8 @@ function ensureProjectStatus(value) {
18053
18479
  var WorkStorage = class {
18054
18480
  constructor(memoryDir) {
18055
18481
  this.memoryDir = memoryDir;
18056
- this.tasksDir = path28.join(memoryDir, "work", "tasks");
18057
- this.projectsDir = path28.join(memoryDir, "work", "projects");
18482
+ this.tasksDir = path29.join(memoryDir, "work", "tasks");
18483
+ this.projectsDir = path29.join(memoryDir, "work", "projects");
18058
18484
  }
18059
18485
  tasksDir;
18060
18486
  projectsDir;
@@ -18064,11 +18490,11 @@ var WorkStorage = class {
18064
18490
  }
18065
18491
  taskPath(id) {
18066
18492
  assertValidWorkId(id, "task");
18067
- return path28.join(this.tasksDir, `${id}.md`);
18493
+ return path29.join(this.tasksDir, `${id}.md`);
18068
18494
  }
18069
18495
  projectPath(id) {
18070
18496
  assertValidWorkId(id, "project");
18071
- return path28.join(this.projectsDir, `${id}.md`);
18497
+ return path29.join(this.projectsDir, `${id}.md`);
18072
18498
  }
18073
18499
  serializeTask(task) {
18074
18500
  return `${serializeFrontmatter2(task)}
@@ -18140,7 +18566,7 @@ ${project.description}
18140
18566
  throw new Error(`project not found: ${task.projectId}`);
18141
18567
  }
18142
18568
  }
18143
- await writeFile19(this.taskPath(task.id), this.serializeTask(task), "utf-8");
18569
+ await writeFile20(this.taskPath(task.id), this.serializeTask(task), "utf-8");
18144
18570
  if (task.projectId) {
18145
18571
  await this.addTaskIdToProject(task.projectId, task.id, now);
18146
18572
  }
@@ -18148,7 +18574,7 @@ ${project.description}
18148
18574
  }
18149
18575
  async getTask(id) {
18150
18576
  try {
18151
- const raw = await readFile20(this.taskPath(id), "utf-8");
18577
+ const raw = await readFile21(this.taskPath(id), "utf-8");
18152
18578
  return this.parseTask(raw);
18153
18579
  } catch {
18154
18580
  return null;
@@ -18156,11 +18582,11 @@ ${project.description}
18156
18582
  }
18157
18583
  async listTasks(filter) {
18158
18584
  await this.ensureDirectories();
18159
- const entries = await readdir12(this.tasksDir, { withFileTypes: true });
18585
+ const entries = await readdir13(this.tasksDir, { withFileTypes: true });
18160
18586
  const out = [];
18161
18587
  for (const entry of entries) {
18162
18588
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
18163
- const raw = await readFile20(path28.join(this.tasksDir, entry.name), "utf-8");
18589
+ const raw = await readFile21(path29.join(this.tasksDir, entry.name), "utf-8");
18164
18590
  const task = this.parseTask(raw);
18165
18591
  if (!task) continue;
18166
18592
  if (filter?.status && task.status !== filter.status) continue;
@@ -18202,7 +18628,7 @@ ${project.description}
18202
18628
  tags: patch.tags ?? existing.tags,
18203
18629
  updatedAt: now.toISOString()
18204
18630
  };
18205
- await writeFile19(this.taskPath(id), this.serializeTask(next), "utf-8");
18631
+ await writeFile20(this.taskPath(id), this.serializeTask(next), "utf-8");
18206
18632
  return next;
18207
18633
  }
18208
18634
  async transitionTask(id, nextStatus, now = /* @__PURE__ */ new Date()) {
@@ -18242,12 +18668,12 @@ ${project.description}
18242
18668
  createdAt: timestamp,
18243
18669
  updatedAt: timestamp
18244
18670
  };
18245
- await writeFile19(this.projectPath(project.id), this.serializeProject(project), "utf-8");
18671
+ await writeFile20(this.projectPath(project.id), this.serializeProject(project), "utf-8");
18246
18672
  return project;
18247
18673
  }
18248
18674
  async getProject(id) {
18249
18675
  try {
18250
- const raw = await readFile20(this.projectPath(id), "utf-8");
18676
+ const raw = await readFile21(this.projectPath(id), "utf-8");
18251
18677
  return this.parseProject(raw);
18252
18678
  } catch {
18253
18679
  return null;
@@ -18255,11 +18681,11 @@ ${project.description}
18255
18681
  }
18256
18682
  async listProjects() {
18257
18683
  await this.ensureDirectories();
18258
- const entries = await readdir12(this.projectsDir, { withFileTypes: true });
18684
+ const entries = await readdir13(this.projectsDir, { withFileTypes: true });
18259
18685
  const out = [];
18260
18686
  for (const entry of entries) {
18261
18687
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
18262
- const raw = await readFile20(path28.join(this.projectsDir, entry.name), "utf-8");
18688
+ const raw = await readFile21(path29.join(this.projectsDir, entry.name), "utf-8");
18263
18689
  const project = this.parseProject(raw);
18264
18690
  if (project) out.push(project);
18265
18691
  }
@@ -18276,7 +18702,7 @@ ${project.description}
18276
18702
  taskIds: patch.taskIds ? [...patch.taskIds].sort() : existing.taskIds,
18277
18703
  updatedAt: now.toISOString()
18278
18704
  };
18279
- await writeFile19(this.projectPath(id), this.serializeProject(next), "utf-8");
18705
+ await writeFile20(this.projectPath(id), this.serializeProject(next), "utf-8");
18280
18706
  return next;
18281
18707
  }
18282
18708
  async deleteProject(id) {
@@ -19815,7 +20241,7 @@ Best for:
19815
20241
  - Reviewing identity development over time`,
19816
20242
  parameters: Type.Object({}),
19817
20243
  async execute() {
19818
- const workspaceDir = path29.join(process.env.HOME ?? "~", ".openclaw", "workspace");
20244
+ const workspaceDir = path30.join(process.env.HOME ?? "~", ".openclaw", "workspace");
19819
20245
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
19820
20246
  if (!identity) {
19821
20247
  return toolResult("No identity file found. Identity reflections build automatically through conversations when identityEnabled is true.");
@@ -20332,13 +20758,13 @@ promotionCandidates: ${res.promotionCandidateCount}`
20332
20758
  }
20333
20759
 
20334
20760
  // src/cli.ts
20335
- import path45 from "path";
20336
- import { access as access3, readFile as readFile31, readdir as readdir19, unlink as unlink5 } from "fs/promises";
20337
- import { createHash as createHash9 } from "crypto";
20761
+ import path48 from "path";
20762
+ import { access as access3, readFile as readFile34, readdir as readdir20, unlink as unlink6 } from "fs/promises";
20763
+ import { createHash as createHash10 } from "crypto";
20338
20764
 
20339
20765
  // src/transfer/export-json.ts
20340
- import path31 from "path";
20341
- import { mkdir as mkdir23, readFile as readFile22 } from "fs/promises";
20766
+ import path32 from "path";
20767
+ import { mkdir as mkdir23, readFile as readFile23 } from "fs/promises";
20342
20768
 
20343
20769
  // src/transfer/constants.ts
20344
20770
  var EXPORT_FORMAT = "openclaw-engram-export";
@@ -20346,10 +20772,10 @@ var EXPORT_SCHEMA_VERSION = 1;
20346
20772
 
20347
20773
  // src/transfer/fs-utils.ts
20348
20774
  import { createHash as createHash8 } from "crypto";
20349
- import { mkdir as mkdir22, readdir as readdir13, readFile as readFile21, stat as stat6, writeFile as writeFile20 } from "fs/promises";
20350
- import path30 from "path";
20775
+ import { mkdir as mkdir22, readdir as readdir14, readFile as readFile22, stat as stat6, writeFile as writeFile21 } from "fs/promises";
20776
+ import path31 from "path";
20351
20777
  async function sha256File(filePath) {
20352
- const buf = await readFile21(filePath);
20778
+ const buf = await readFile22(filePath);
20353
20779
  const sha256 = createHash8("sha256").update(buf).digest("hex");
20354
20780
  return { sha256, bytes: buf.byteLength };
20355
20781
  }
@@ -20359,19 +20785,19 @@ function sha256String(content) {
20359
20785
  return { sha256, bytes: buf.byteLength };
20360
20786
  }
20361
20787
  async function writeJsonFile(filePath, value) {
20362
- await mkdir22(path30.dirname(filePath), { recursive: true });
20363
- await writeFile20(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
20788
+ await mkdir22(path31.dirname(filePath), { recursive: true });
20789
+ await writeFile21(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
20364
20790
  }
20365
20791
  async function readJsonFile(filePath) {
20366
- const raw = await readFile21(filePath, "utf-8");
20792
+ const raw = await readFile22(filePath, "utf-8");
20367
20793
  return JSON.parse(raw);
20368
20794
  }
20369
20795
  async function listFilesRecursive(rootDir) {
20370
20796
  const out = [];
20371
20797
  async function walk(dir) {
20372
- const entries = await readdir13(dir, { withFileTypes: true });
20798
+ const entries = await readdir14(dir, { withFileTypes: true });
20373
20799
  for (const ent of entries) {
20374
- const fp = path30.join(dir, ent.name);
20800
+ const fp = path31.join(dir, ent.name);
20375
20801
  if (ent.isDirectory()) {
20376
20802
  await walk(fp);
20377
20803
  } else if (ent.isFile()) {
@@ -20391,11 +20817,11 @@ async function fileExists(filePath) {
20391
20817
  }
20392
20818
  }
20393
20819
  function toPosixRelPath(absPath, rootDir) {
20394
- const rel = path30.relative(rootDir, absPath);
20395
- return rel.split(path30.sep).join("/");
20820
+ const rel = path31.relative(rootDir, absPath);
20821
+ return rel.split(path31.sep).join("/");
20396
20822
  }
20397
20823
  function fromPosixRelPath(relPath) {
20398
- return relPath.split("/").join(path30.sep);
20824
+ return relPath.split("/").join(path31.sep);
20399
20825
  }
20400
20826
 
20401
20827
  // src/transfer/export-json.ts
@@ -20411,24 +20837,24 @@ function shouldExclude(relPosix, includeTranscripts) {
20411
20837
  }
20412
20838
  async function exportJsonBundle(opts) {
20413
20839
  const includeTranscripts = opts.includeTranscripts === true;
20414
- const outDirAbs = path31.resolve(opts.outDir);
20840
+ const outDirAbs = path32.resolve(opts.outDir);
20415
20841
  await mkdir23(outDirAbs, { recursive: true });
20416
- const memoryDirAbs = path31.resolve(opts.memoryDir);
20842
+ const memoryDirAbs = path32.resolve(opts.memoryDir);
20417
20843
  const filesAbs = await listFilesRecursive(memoryDirAbs);
20418
20844
  const records = [];
20419
20845
  const manifestFiles = [];
20420
20846
  for (const abs of filesAbs) {
20421
20847
  const relPosix = toPosixRelPath(abs, memoryDirAbs);
20422
20848
  if (shouldExclude(relPosix, includeTranscripts)) continue;
20423
- const content = await readFile22(abs, "utf-8");
20849
+ const content = await readFile23(abs, "utf-8");
20424
20850
  records.push({ path: relPosix, content });
20425
20851
  const { sha256, bytes } = await sha256File(abs);
20426
20852
  manifestFiles.push({ path: relPosix, sha256, bytes });
20427
20853
  }
20428
20854
  if (opts.includeWorkspaceIdentity !== false && opts.workspaceDir) {
20429
- const identityPath = path31.join(opts.workspaceDir, "IDENTITY.md");
20855
+ const identityPath = path32.join(opts.workspaceDir, "IDENTITY.md");
20430
20856
  try {
20431
- const content = await readFile22(identityPath, "utf-8");
20857
+ const content = await readFile23(identityPath, "utf-8");
20432
20858
  const relPath = "workspace/IDENTITY.md";
20433
20859
  records.push({ path: relPath, content });
20434
20860
  const { sha256, bytes } = sha256String(content);
@@ -20445,13 +20871,13 @@ async function exportJsonBundle(opts) {
20445
20871
  files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
20446
20872
  };
20447
20873
  const bundle = { manifest, records };
20448
- await writeJsonFile(path31.join(outDirAbs, "manifest.json"), manifest);
20449
- await writeJsonFile(path31.join(outDirAbs, "bundle.json"), bundle);
20874
+ await writeJsonFile(path32.join(outDirAbs, "manifest.json"), manifest);
20875
+ await writeJsonFile(path32.join(outDirAbs, "bundle.json"), bundle);
20450
20876
  }
20451
20877
 
20452
20878
  // src/transfer/export-md.ts
20453
- import path32 from "path";
20454
- import { mkdir as mkdir24, readFile as readFile23, writeFile as writeFile21 } from "fs/promises";
20879
+ import path33 from "path";
20880
+ import { mkdir as mkdir24, readFile as readFile24, writeFile as writeFile22 } from "fs/promises";
20455
20881
  function shouldExclude2(relPosix, includeTranscripts) {
20456
20882
  const parts = relPosix.split("/");
20457
20883
  if (!includeTranscripts && parts[0] === "transcripts") return true;
@@ -20459,18 +20885,18 @@ function shouldExclude2(relPosix, includeTranscripts) {
20459
20885
  }
20460
20886
  async function exportMarkdownBundle(opts) {
20461
20887
  const includeTranscripts = opts.includeTranscripts === true;
20462
- const outDirAbs = path32.resolve(opts.outDir);
20888
+ const outDirAbs = path33.resolve(opts.outDir);
20463
20889
  await mkdir24(outDirAbs, { recursive: true });
20464
- const memDirAbs = path32.resolve(opts.memoryDir);
20890
+ const memDirAbs = path33.resolve(opts.memoryDir);
20465
20891
  const filesAbs = await listFilesRecursive(memDirAbs);
20466
20892
  const manifestFiles = [];
20467
20893
  for (const abs of filesAbs) {
20468
20894
  const relPosix = toPosixRelPath(abs, memDirAbs);
20469
20895
  if (shouldExclude2(relPosix, includeTranscripts)) continue;
20470
- const dstAbs = path32.join(outDirAbs, ...relPosix.split("/"));
20471
- await mkdir24(path32.dirname(dstAbs), { recursive: true });
20472
- const content = await readFile23(abs);
20473
- await writeFile21(dstAbs, content);
20896
+ const dstAbs = path33.join(outDirAbs, ...relPosix.split("/"));
20897
+ await mkdir24(path33.dirname(dstAbs), { recursive: true });
20898
+ const content = await readFile24(abs);
20899
+ await writeFile22(dstAbs, content);
20474
20900
  const { sha256, bytes } = await sha256File(abs);
20475
20901
  manifestFiles.push({ path: relPosix, sha256, bytes });
20476
20902
  }
@@ -20482,12 +20908,12 @@ async function exportMarkdownBundle(opts) {
20482
20908
  includesTranscripts: includeTranscripts,
20483
20909
  files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
20484
20910
  };
20485
- await writeJsonFile(path32.join(outDirAbs, "manifest.json"), manifest);
20911
+ await writeJsonFile(path33.join(outDirAbs, "manifest.json"), manifest);
20486
20912
  }
20487
20913
  async function looksLikeEngramMdExport(fromDir) {
20488
- const dirAbs = path32.resolve(fromDir);
20914
+ const dirAbs = path33.resolve(fromDir);
20489
20915
  try {
20490
- const raw = await readFile23(path32.join(dirAbs, "manifest.json"), "utf-8");
20916
+ const raw = await readFile24(path33.join(dirAbs, "manifest.json"), "utf-8");
20491
20917
  const parsed = JSON.parse(raw);
20492
20918
  return parsed.format === EXPORT_FORMAT && parsed.schemaVersion === EXPORT_SCHEMA_VERSION;
20493
20919
  } catch {
@@ -20496,16 +20922,16 @@ async function looksLikeEngramMdExport(fromDir) {
20496
20922
  }
20497
20923
 
20498
20924
  // src/transfer/backup.ts
20499
- import path33 from "path";
20500
- import { mkdir as mkdir25, readdir as readdir14, rm as rm4 } from "fs/promises";
20925
+ import path34 from "path";
20926
+ import { mkdir as mkdir25, readdir as readdir15, rm as rm4 } from "fs/promises";
20501
20927
  function timestampDirName(now) {
20502
20928
  return now.toISOString().replace(/[:.]/g, "-");
20503
20929
  }
20504
20930
  async function backupMemoryDir(opts) {
20505
- const outDirAbs = path33.resolve(opts.outDir);
20931
+ const outDirAbs = path34.resolve(opts.outDir);
20506
20932
  await mkdir25(outDirAbs, { recursive: true });
20507
20933
  const ts = timestampDirName(/* @__PURE__ */ new Date());
20508
- const backupDir = path33.join(outDirAbs, ts);
20934
+ const backupDir = path34.join(outDirAbs, ts);
20509
20935
  await exportMarkdownBundle({
20510
20936
  memoryDir: opts.memoryDir,
20511
20937
  outDir: backupDir,
@@ -20518,7 +20944,7 @@ async function backupMemoryDir(opts) {
20518
20944
  return backupDir;
20519
20945
  }
20520
20946
  async function enforceRetention(outDirAbs, retentionDays) {
20521
- const entries = await readdir14(outDirAbs, { withFileTypes: true });
20947
+ const entries = await readdir15(outDirAbs, { withFileTypes: true });
20522
20948
  const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
20523
20949
  for (const ent of entries) {
20524
20950
  if (!ent.isDirectory()) continue;
@@ -20530,15 +20956,15 @@ async function enforceRetention(outDirAbs, retentionDays) {
20530
20956
  const tsMs = iso ? Date.parse(iso) : NaN;
20531
20957
  if (!Number.isFinite(tsMs)) continue;
20532
20958
  if (tsMs < cutoffMs) {
20533
- await rm4(path33.join(outDirAbs, name), { recursive: true, force: true });
20959
+ await rm4(path34.join(outDirAbs, name), { recursive: true, force: true });
20534
20960
  }
20535
20961
  }
20536
20962
  }
20537
20963
 
20538
20964
  // src/transfer/export-sqlite.ts
20539
- import path34 from "path";
20965
+ import path35 from "path";
20540
20966
  import Database from "better-sqlite3";
20541
- import { readFile as readFile24 } from "fs/promises";
20967
+ import { readFile as readFile25 } from "fs/promises";
20542
20968
 
20543
20969
  // src/transfer/sqlite-schema.ts
20544
20970
  var SQLITE_SCHEMA_VERSION = 1;
@@ -20564,8 +20990,8 @@ function shouldExclude3(relPosix, includeTranscripts) {
20564
20990
  }
20565
20991
  async function exportSqlite(opts) {
20566
20992
  const includeTranscripts = opts.includeTranscripts === true;
20567
- const memDirAbs = path34.resolve(opts.memoryDir);
20568
- const outAbs = path34.resolve(opts.outFile);
20993
+ const memDirAbs = path35.resolve(opts.memoryDir);
20994
+ const outAbs = path35.resolve(opts.outFile);
20569
20995
  const filesAbs = await listFilesRecursive(memDirAbs);
20570
20996
  const db = new Database(outAbs);
20571
20997
  try {
@@ -20586,7 +21012,7 @@ async function exportSqlite(opts) {
20586
21012
  for (const abs of filesAbs) {
20587
21013
  const relPosix = toPosixRelPath(abs, memDirAbs);
20588
21014
  if (shouldExclude3(relPosix, includeTranscripts)) continue;
20589
- const content = await readFile24(abs, "utf-8");
21015
+ const content = await readFile25(abs, "utf-8");
20590
21016
  const { sha256, bytes } = await sha256File(abs);
20591
21017
  rows.push({ rel: relPosix, bytes, sha256, content });
20592
21018
  }
@@ -20597,8 +21023,8 @@ async function exportSqlite(opts) {
20597
21023
  }
20598
21024
 
20599
21025
  // src/transfer/import-json.ts
20600
- import path35 from "path";
20601
- import { mkdir as mkdir26, writeFile as writeFile22 } from "fs/promises";
21026
+ import path36 from "path";
21027
+ import { mkdir as mkdir26, writeFile as writeFile23 } from "fs/promises";
20602
21028
 
20603
21029
  // src/transfer/types.ts
20604
21030
  import { z as z4 } from "zod";
@@ -20631,21 +21057,21 @@ function normalizeForDedupe(s) {
20631
21057
  }
20632
21058
  async function importJsonBundle(opts) {
20633
21059
  const conflict = opts.conflict ?? "skip";
20634
- const fromDirAbs = path35.resolve(opts.fromDir);
20635
- const bundlePath = path35.join(fromDirAbs, "bundle.json");
21060
+ const fromDirAbs = path36.resolve(opts.fromDir);
21061
+ const bundlePath = path36.join(fromDirAbs, "bundle.json");
20636
21062
  const bundle = ExportBundleV1Schema.parse(await readJsonFile(bundlePath));
20637
- const memDirAbs = path35.resolve(opts.targetMemoryDir);
21063
+ const memDirAbs = path36.resolve(opts.targetMemoryDir);
20638
21064
  const written = [];
20639
21065
  let skipped = 0;
20640
21066
  for (const rec of bundle.records) {
20641
21067
  const isWorkspace = rec.path.startsWith("workspace/");
20642
- const targetBase = isWorkspace ? opts.workspaceDir ? path35.resolve(opts.workspaceDir) : null : memDirAbs;
21068
+ const targetBase = isWorkspace ? opts.workspaceDir ? path36.resolve(opts.workspaceDir) : null : memDirAbs;
20643
21069
  if (isWorkspace && !targetBase) {
20644
21070
  skipped += 1;
20645
21071
  continue;
20646
21072
  }
20647
21073
  const relFs = fromPosixRelPath(isWorkspace ? rec.path.replace(/^workspace\//, "") : rec.path);
20648
- const absTarget = path35.join(targetBase, relFs);
21074
+ const absTarget = path36.join(targetBase, relFs);
20649
21075
  const exists3 = await fileExists(absTarget);
20650
21076
  if (exists3) {
20651
21077
  if (conflict === "skip") {
@@ -20669,30 +21095,30 @@ async function importJsonBundle(opts) {
20669
21095
  return { written: 0, skipped };
20670
21096
  }
20671
21097
  for (const w of written) {
20672
- await mkdir26(path35.dirname(w.abs), { recursive: true });
20673
- await writeFile22(w.abs, w.content, "utf-8");
21098
+ await mkdir26(path36.dirname(w.abs), { recursive: true });
21099
+ await writeFile23(w.abs, w.content, "utf-8");
20674
21100
  }
20675
21101
  return { written: written.length, skipped };
20676
21102
  }
20677
21103
  function looksLikeEngramJsonExport(fromDir) {
20678
- const dir = path35.resolve(fromDir);
21104
+ const dir = path36.resolve(fromDir);
20679
21105
  return Promise.all([
20680
- fileExists(path35.join(dir, "manifest.json")),
20681
- fileExists(path35.join(dir, "bundle.json"))
21106
+ fileExists(path36.join(dir, "manifest.json")),
21107
+ fileExists(path36.join(dir, "bundle.json"))
20682
21108
  ]).then(([m, b]) => m && b);
20683
21109
  }
20684
21110
 
20685
21111
  // src/transfer/import-sqlite.ts
20686
- import path36 from "path";
21112
+ import path37 from "path";
20687
21113
  import Database2 from "better-sqlite3";
20688
- import { mkdir as mkdir27, writeFile as writeFile23 } from "fs/promises";
21114
+ import { mkdir as mkdir27, writeFile as writeFile24 } from "fs/promises";
20689
21115
  function normalizeForDedupe2(s) {
20690
21116
  return s.replace(/\s+/g, " ").trim();
20691
21117
  }
20692
21118
  async function importSqlite(opts) {
20693
21119
  const conflict = opts.conflict ?? "skip";
20694
- const memDirAbs = path36.resolve(opts.targetMemoryDir);
20695
- const fromAbs = path36.resolve(opts.fromFile);
21120
+ const memDirAbs = path37.resolve(opts.targetMemoryDir);
21121
+ const fromAbs = path37.resolve(opts.fromFile);
20696
21122
  const db = new Database2(fromAbs, { readonly: true });
20697
21123
  const written = [];
20698
21124
  let skipped = 0;
@@ -20705,7 +21131,7 @@ async function importSqlite(opts) {
20705
21131
  const rows = db.prepare("SELECT path_rel, content FROM files").all();
20706
21132
  for (const r of rows) {
20707
21133
  const relFs = fromPosixRelPath(r.path_rel);
20708
- const absTarget = path36.join(memDirAbs, relFs);
21134
+ const absTarget = path37.join(memDirAbs, relFs);
20709
21135
  const exists3 = await fileExists(absTarget);
20710
21136
  if (exists3) {
20711
21137
  if (conflict === "skip") {
@@ -20730,30 +21156,30 @@ async function importSqlite(opts) {
20730
21156
  }
20731
21157
  if (opts.dryRun) return { written: 0, skipped };
20732
21158
  for (const w of written) {
20733
- await mkdir27(path36.dirname(w.abs), { recursive: true });
20734
- await writeFile23(w.abs, w.content, "utf-8");
21159
+ await mkdir27(path37.dirname(w.abs), { recursive: true });
21160
+ await writeFile24(w.abs, w.content, "utf-8");
20735
21161
  }
20736
21162
  return { written: written.length, skipped };
20737
21163
  }
20738
21164
 
20739
21165
  // src/transfer/import-md.ts
20740
- import path37 from "path";
20741
- import { mkdir as mkdir28, readFile as readFile25, writeFile as writeFile24 } from "fs/promises";
21166
+ import path38 from "path";
21167
+ import { mkdir as mkdir28, readFile as readFile26, writeFile as writeFile25 } from "fs/promises";
20742
21168
  function normalizeForDedupe3(s) {
20743
21169
  return s.replace(/\s+/g, " ").trim();
20744
21170
  }
20745
21171
  async function importMarkdownBundle(opts) {
20746
21172
  const conflict = opts.conflict ?? "skip";
20747
- const fromAbs = path37.resolve(opts.fromDir);
20748
- const targetAbs = path37.resolve(opts.targetMemoryDir);
21173
+ const fromAbs = path38.resolve(opts.fromDir);
21174
+ const targetAbs = path38.resolve(opts.targetMemoryDir);
20749
21175
  const filesAbs = await listFilesRecursive(fromAbs);
20750
21176
  const writes = [];
20751
21177
  let skipped = 0;
20752
21178
  for (const abs of filesAbs) {
20753
21179
  const relPosix = toPosixRelPath(abs, fromAbs);
20754
21180
  if (relPosix === "manifest.json") continue;
20755
- const dstAbs = path37.join(targetAbs, fromPosixRelPath(relPosix));
20756
- const content = await readFile25(abs, "utf-8");
21181
+ const dstAbs = path38.join(targetAbs, fromPosixRelPath(relPosix));
21182
+ const content = await readFile26(abs, "utf-8");
20757
21183
  const exists3 = await fileExists(dstAbs);
20758
21184
  if (exists3) {
20759
21185
  if (conflict === "skip") {
@@ -20775,17 +21201,17 @@ async function importMarkdownBundle(opts) {
20775
21201
  }
20776
21202
  if (opts.dryRun) return { written: 0, skipped };
20777
21203
  for (const w of writes) {
20778
- await mkdir28(path37.dirname(w.abs), { recursive: true });
20779
- await writeFile24(w.abs, w.content, "utf-8");
21204
+ await mkdir28(path38.dirname(w.abs), { recursive: true });
21205
+ await writeFile25(w.abs, w.content, "utf-8");
20780
21206
  }
20781
21207
  return { written: writes.length, skipped };
20782
21208
  }
20783
21209
 
20784
21210
  // src/transfer/autodetect.ts
20785
- import path38 from "path";
21211
+ import path39 from "path";
20786
21212
  import { stat as stat7 } from "fs/promises";
20787
21213
  async function detectImportFormat(fromPath) {
20788
- const abs = path38.resolve(fromPath);
21214
+ const abs = path39.resolve(fromPath);
20789
21215
  let st;
20790
21216
  try {
20791
21217
  st = await stat7(abs);
@@ -21213,8 +21639,8 @@ function gatherCandidates(input, warnings) {
21213
21639
  const record = rec;
21214
21640
  const content = typeof record.content === "string" ? record.content : null;
21215
21641
  if (!content) continue;
21216
- const path47 = typeof record.path === "string" ? record.path : "";
21217
- if (!path47.startsWith("transcripts/") && !path47.includes("/transcripts/")) continue;
21642
+ const path50 = typeof record.path === "string" ? record.path : "";
21643
+ if (!path50.startsWith("transcripts/") && !path50.includes("/transcripts/")) continue;
21218
21644
  rows.push(...parseJsonl(content, warnings));
21219
21645
  }
21220
21646
  return rows;
@@ -21270,8 +21696,8 @@ var openclawReplayNormalizer = {
21270
21696
  };
21271
21697
 
21272
21698
  // src/maintenance/archive-observations.ts
21273
- import path39 from "path";
21274
- import { mkdir as mkdir29, readdir as readdir15, readFile as readFile26, unlink as unlink4, writeFile as writeFile25 } from "fs/promises";
21699
+ import path40 from "path";
21700
+ import { mkdir as mkdir29, readdir as readdir16, readFile as readFile27, unlink as unlink5, writeFile as writeFile26 } from "fs/promises";
21275
21701
  var DATE_FILE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})\.(jsonl|md)$/;
21276
21702
  function normalizeRetentionDays(value) {
21277
21703
  if (!Number.isFinite(value)) return 30;
@@ -21292,13 +21718,13 @@ async function listFilesRecursive2(root, relPrefix = "") {
21292
21718
  const out = [];
21293
21719
  let entries;
21294
21720
  try {
21295
- entries = await readdir15(root, { withFileTypes: true });
21721
+ entries = await readdir16(root, { withFileTypes: true });
21296
21722
  } catch {
21297
21723
  return out;
21298
21724
  }
21299
21725
  for (const entry of entries) {
21300
- const rel = relPrefix ? path39.join(relPrefix, entry.name) : entry.name;
21301
- const full = path39.join(root, entry.name);
21726
+ const rel = relPrefix ? path40.join(relPrefix, entry.name) : entry.name;
21727
+ const full = path40.join(root, entry.name);
21302
21728
  if (entry.isDirectory()) {
21303
21729
  out.push(...await listFilesRecursive2(full, rel));
21304
21730
  continue;
@@ -21308,19 +21734,19 @@ async function listFilesRecursive2(root, relPrefix = "") {
21308
21734
  return out;
21309
21735
  }
21310
21736
  async function collectArchiveCandidates(memoryDir, cutoffTimeMs) {
21311
- const roots = ["transcripts", path39.join("state", "tool-usage"), path39.join("summaries", "hourly")];
21737
+ const roots = ["transcripts", path40.join("state", "tool-usage"), path40.join("summaries", "hourly")];
21312
21738
  const out = [];
21313
21739
  for (const relRoot of roots) {
21314
- const absRoot = path39.join(memoryDir, relRoot);
21740
+ const absRoot = path40.join(memoryDir, relRoot);
21315
21741
  const files = await listFilesRecursive2(absRoot);
21316
21742
  for (const fileRel of files) {
21317
- const filename = path39.basename(fileRel);
21743
+ const filename = path40.basename(fileRel);
21318
21744
  const parsedDate = extractDateFromFilename(filename);
21319
21745
  if (!parsedDate) continue;
21320
21746
  if (parsedDate.getTime() >= cutoffTimeMs) continue;
21321
21747
  out.push({
21322
- absolutePath: path39.join(absRoot, fileRel),
21323
- relativePath: path39.join(relRoot, fileRel)
21748
+ absolutePath: path40.join(absRoot, fileRel),
21749
+ relativePath: path40.join(relRoot, fileRel)
21324
21750
  });
21325
21751
  }
21326
21752
  }
@@ -21335,7 +21761,7 @@ async function archiveObservations(options) {
21335
21761
  new Date(now.getTime() - retentionDays * 24 * 60 * 60 * 1e3)
21336
21762
  );
21337
21763
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
21338
- const archiveRoot = path39.join(options.memoryDir, "archive", "observations", stamp);
21764
+ const archiveRoot = path40.join(options.memoryDir, "archive", "observations", stamp);
21339
21765
  const candidates = retentionDays === 0 ? [] : await collectArchiveCandidates(
21340
21766
  options.memoryDir,
21341
21767
  cutoffDayStartUtc
@@ -21346,12 +21772,12 @@ async function archiveObservations(options) {
21346
21772
  if (!dryRun && candidates.length > 0) {
21347
21773
  await mkdir29(archiveRoot, { recursive: true });
21348
21774
  for (const candidate of candidates) {
21349
- const archivePath = path39.join(archiveRoot, candidate.relativePath);
21350
- const archiveDir = path39.dirname(archivePath);
21775
+ const archivePath = path40.join(archiveRoot, candidate.relativePath);
21776
+ const archiveDir = path40.dirname(archivePath);
21351
21777
  await mkdir29(archiveDir, { recursive: true });
21352
- const raw = await readFile26(candidate.absolutePath);
21353
- await writeFile25(archivePath, raw);
21354
- await unlink4(candidate.absolutePath);
21778
+ const raw = await readFile27(candidate.absolutePath);
21779
+ await writeFile26(archivePath, raw);
21780
+ await unlink5(candidate.absolutePath);
21355
21781
  archivedFiles += 1;
21356
21782
  archivedBytes += raw.byteLength;
21357
21783
  archivedRelativePaths.push(candidate.relativePath);
@@ -21371,12 +21797,12 @@ async function archiveObservations(options) {
21371
21797
  }
21372
21798
 
21373
21799
  // src/maintenance/rebuild-observations.ts
21374
- import path41 from "path";
21375
- import { readdir as readdir16, readFile as readFile28 } from "fs/promises";
21800
+ import path42 from "path";
21801
+ import { readdir as readdir17, readFile as readFile29 } from "fs/promises";
21376
21802
 
21377
21803
  // src/maintenance/observation-ledger-utils.ts
21378
- import path40 from "path";
21379
- import { mkdir as mkdir30, readFile as readFile27, writeFile as writeFile26 } from "fs/promises";
21804
+ import path41 from "path";
21805
+ import { mkdir as mkdir30, readFile as readFile28, writeFile as writeFile27 } from "fs/promises";
21380
21806
  function toHourBucketIso(timestamp) {
21381
21807
  const normalized = /(?:Z|[+-]\d{2}:\d{2})$/u.test(timestamp) ? timestamp : `${timestamp}Z`;
21382
21808
  const ms = Date.parse(normalized);
@@ -21387,17 +21813,17 @@ function toHourBucketIso(timestamp) {
21387
21813
  }
21388
21814
  async function backupAndWriteRebuiltObservations(options) {
21389
21815
  const stamp = options.now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
21390
- const archiveRoot = path40.join(options.memoryDir, "archive", "observations", stamp);
21391
- let backupPath = path40.join(
21816
+ const archiveRoot = path41.join(options.memoryDir, "archive", "observations", stamp);
21817
+ let backupPath = path41.join(
21392
21818
  archiveRoot,
21393
21819
  "state",
21394
21820
  "observation-ledger",
21395
21821
  "rebuilt-observations.jsonl"
21396
21822
  );
21397
21823
  try {
21398
- const existing = await readFile27(options.outputPath, "utf-8");
21399
- await mkdir30(path40.dirname(backupPath), { recursive: true });
21400
- await writeFile26(backupPath, existing, "utf-8");
21824
+ const existing = await readFile28(options.outputPath, "utf-8");
21825
+ await mkdir30(path41.dirname(backupPath), { recursive: true });
21826
+ await writeFile27(backupPath, existing, "utf-8");
21401
21827
  } catch (err) {
21402
21828
  const code = err.code;
21403
21829
  if (code && code === "ENOENT") {
@@ -21413,8 +21839,8 @@ async function backupAndWriteRebuiltObservations(options) {
21413
21839
  rebuiltAt
21414
21840
  })
21415
21841
  );
21416
- await mkdir30(path40.dirname(options.outputPath), { recursive: true });
21417
- await writeFile26(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
21842
+ await mkdir30(path41.dirname(options.outputPath), { recursive: true });
21843
+ await writeFile27(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
21418
21844
  ` : "", "utf-8");
21419
21845
  return backupPath;
21420
21846
  }
@@ -21423,11 +21849,11 @@ async function backupAndWriteRebuiltObservations(options) {
21423
21849
  function toSortableKey(sessionKey, hour) {
21424
21850
  return `${sessionKey}\0${hour}`;
21425
21851
  }
21426
- async function listTranscriptFiles(root) {
21852
+ async function listTranscriptFiles2(root) {
21427
21853
  const out = [];
21428
21854
  let entries;
21429
21855
  try {
21430
- entries = await readdir16(root, { withFileTypes: true });
21856
+ entries = await readdir17(root, { withFileTypes: true });
21431
21857
  } catch (err) {
21432
21858
  const code = err.code;
21433
21859
  if (code && code === "ENOENT") return out;
@@ -21436,9 +21862,9 @@ async function listTranscriptFiles(root) {
21436
21862
  for (const entry of entries) {
21437
21863
  if (entry.name === "." || entry.name === "..") continue;
21438
21864
  if (entry.isSymbolicLink()) continue;
21439
- const full = path41.join(root, entry.name);
21865
+ const full = path42.join(root, entry.name);
21440
21866
  if (entry.isDirectory()) {
21441
- out.push(...await listTranscriptFiles(full));
21867
+ out.push(...await listTranscriptFiles2(full));
21442
21868
  continue;
21443
21869
  }
21444
21870
  if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -21496,18 +21922,18 @@ function buildLedgerRows(linesByFile) {
21496
21922
  async function rebuildObservations(options) {
21497
21923
  const dryRun = options.dryRun !== false;
21498
21924
  const now = options.now ?? /* @__PURE__ */ new Date();
21499
- const transcriptsRoot = path41.join(options.memoryDir, "transcripts");
21500
- const outputPath = path41.join(
21925
+ const transcriptsRoot = path42.join(options.memoryDir, "transcripts");
21926
+ const outputPath = path42.join(
21501
21927
  options.memoryDir,
21502
21928
  "state",
21503
21929
  "observation-ledger",
21504
21930
  "rebuilt-observations.jsonl"
21505
21931
  );
21506
- const transcriptFiles = await listTranscriptFiles(transcriptsRoot);
21932
+ const transcriptFiles = await listTranscriptFiles2(transcriptsRoot);
21507
21933
  const contents = [];
21508
21934
  for (const file of transcriptFiles) {
21509
21935
  try {
21510
- contents.push(await readFile28(file, "utf-8"));
21936
+ contents.push(await readFile29(file, "utf-8"));
21511
21937
  } catch {
21512
21938
  }
21513
21939
  }
@@ -21533,8 +21959,8 @@ async function rebuildObservations(options) {
21533
21959
  }
21534
21960
 
21535
21961
  // src/maintenance/migrate-observations.ts
21536
- import path42 from "path";
21537
- import { readdir as readdir17, readFile as readFile29 } from "fs/promises";
21962
+ import path43 from "path";
21963
+ import { readdir as readdir18, readFile as readFile30 } from "fs/promises";
21538
21964
  function toNonNegativeInt(value) {
21539
21965
  if (typeof value !== "number" || !Number.isFinite(value)) return null;
21540
21966
  const normalized = Math.floor(value);
@@ -21585,7 +22011,7 @@ function toCounts(row) {
21585
22011
  async function listLegacyObservationFiles(root) {
21586
22012
  let entries;
21587
22013
  try {
21588
- entries = await readdir17(root, { withFileTypes: true });
22014
+ entries = await readdir18(root, { withFileTypes: true });
21589
22015
  } catch (err) {
21590
22016
  const code = err.code;
21591
22017
  if (code && code === "ENOENT") return [];
@@ -21597,16 +22023,16 @@ async function listLegacyObservationFiles(root) {
21597
22023
  async function migrateObservations(options) {
21598
22024
  const dryRun = options.dryRun !== false;
21599
22025
  const now = options.now ?? /* @__PURE__ */ new Date();
21600
- const ledgerRoot = path42.join(options.memoryDir, "state", "observation-ledger");
21601
- const outputPath = path42.join(ledgerRoot, "rebuilt-observations.jsonl");
22026
+ const ledgerRoot = path43.join(options.memoryDir, "state", "observation-ledger");
22027
+ const outputPath = path43.join(ledgerRoot, "rebuilt-observations.jsonl");
21602
22028
  const legacyFiles = await listLegacyObservationFiles(ledgerRoot);
21603
- const sourceRelativePaths = legacyFiles.map((name) => path42.join("state", "observation-ledger", name));
22029
+ const sourceRelativePaths = legacyFiles.map((name) => path43.join("state", "observation-ledger", name));
21604
22030
  const byKey = /* @__PURE__ */ new Map();
21605
22031
  let parsedRows = 0;
21606
22032
  let malformedLines = 0;
21607
22033
  for (const file of legacyFiles) {
21608
- const full = path42.join(ledgerRoot, file);
21609
- const raw = await readFile29(full, "utf-8");
22034
+ const full = path43.join(ledgerRoot, file);
22035
+ const raw = await readFile30(full, "utf-8");
21610
22036
  for (const line of raw.split("\n")) {
21611
22037
  if (!line.trim()) continue;
21612
22038
  let parsed;
@@ -21795,10 +22221,10 @@ var defaultCommandRunner = (command, args, options) => {
21795
22221
 
21796
22222
  // src/network/webdav.ts
21797
22223
  import { createReadStream } from "fs";
21798
- import { mkdir as mkdir31, readdir as readdir18, realpath as realpath2, stat as stat9 } from "fs/promises";
22224
+ import { mkdir as mkdir31, readdir as readdir19, realpath as realpath2, stat as stat9 } from "fs/promises";
21799
22225
  import { createServer } from "http";
21800
22226
  import { timingSafeEqual } from "crypto";
21801
- import path43 from "path";
22227
+ import path44 from "path";
21802
22228
  import { pipeline } from "stream/promises";
21803
22229
  import { URL as URL2 } from "url";
21804
22230
  function hostToUrlAuthority(host) {
@@ -21834,10 +22260,10 @@ var WebDavServer = class _WebDavServer {
21834
22260
  const allowedRoots = [];
21835
22261
  const aliasSet = /* @__PURE__ */ new Set();
21836
22262
  for (const dir of options.allowlistDirs) {
21837
- const resolved = path43.resolve(dir);
22263
+ const resolved = path44.resolve(dir);
21838
22264
  await mkdir31(resolved, { recursive: true });
21839
22265
  const canonical = await realpath2(resolved);
21840
- const alias = path43.basename(canonical) || "root";
22266
+ const alias = path44.basename(canonical) || "root";
21841
22267
  if (aliasSet.has(alias)) {
21842
22268
  throw new Error(`duplicate webdav allowlist alias: ${alias}`);
21843
22269
  }
@@ -21993,7 +22419,7 @@ var WebDavServer = class _WebDavServer {
21993
22419
  if (decodedPath.includes("\0")) {
21994
22420
  return { ok: false, code: 400, message: "invalid path" };
21995
22421
  }
21996
- const normalized = path43.posix.normalize(decodedPath);
22422
+ const normalized = path44.posix.normalize(decodedPath);
21997
22423
  const segments = normalized.split("/").filter((segment) => segment.length > 0);
21998
22424
  if (segments.length === 0) {
21999
22425
  return { ok: false, code: 403, message: "root listing is not allowed" };
@@ -22007,7 +22433,7 @@ var WebDavServer = class _WebDavServer {
22007
22433
  if (relative.some((segment) => segment === ".." || segment.includes("\\"))) {
22008
22434
  return { ok: false, code: 403, message: "path traversal is not allowed" };
22009
22435
  }
22010
- const candidate = path43.resolve(root.absolute, ...relative);
22436
+ const candidate = path44.resolve(root.absolute, ...relative);
22011
22437
  if (!this.isPathInside(root.absolute, candidate)) {
22012
22438
  return { ok: false, code: 403, message: "path escaped allowlist" };
22013
22439
  }
@@ -22063,7 +22489,7 @@ var WebDavServer = class _WebDavServer {
22063
22489
  }
22064
22490
  const entries = [];
22065
22491
  if (info.isDirectory()) {
22066
- const children = await readdir18(absolutePath, { withFileTypes: true });
22492
+ const children = await readdir19(absolutePath, { withFileTypes: true });
22067
22493
  for (const child of children) {
22068
22494
  const childHref = toEncodedHref(`${displayPath.replace(/\/$/, "")}/${child.name}`);
22069
22495
  entries.push(`
@@ -22085,10 +22511,10 @@ var WebDavServer = class _WebDavServer {
22085
22511
  }
22086
22512
  isPathInside(root, target) {
22087
22513
  if (target === root) return true;
22088
- if (root === path43.parse(root).root) {
22514
+ if (root === path44.parse(root).root) {
22089
22515
  return target.startsWith(root);
22090
22516
  }
22091
- return target.startsWith(`${root}${path43.sep}`);
22517
+ return target.startsWith(`${root}${path44.sep}`);
22092
22518
  }
22093
22519
  };
22094
22520
  function xmlEscape(value) {
@@ -22098,9 +22524,362 @@ function toEncodedHref(pathname) {
22098
22524
  return pathname.split("/").map((segment) => encodeURIComponent(segment)).join("/");
22099
22525
  }
22100
22526
 
22527
+ // src/dashboard-runtime.ts
22528
+ import { createHash as createHash9 } from "crypto";
22529
+ import { createServer as createServer2 } from "http";
22530
+ import { watch } from "fs";
22531
+ import { readFile as readFile32 } from "fs/promises";
22532
+ import path46 from "path";
22533
+
22534
+ // src/graph-dashboard-parser.ts
22535
+ import path45 from "path";
22536
+ import { readFile as readFile31 } from "fs/promises";
22537
+
22538
+ // src/graph-dashboard-key.ts
22539
+ function graphEdgeKey(edge) {
22540
+ return `${edge.type}|${edge.from}|${edge.to}|${edge.label}|${edge.ts}`;
22541
+ }
22542
+
22543
+ // src/graph-dashboard-parser.ts
22544
+ var GRAPH_TYPES = ["entity", "time", "causal"];
22545
+ function graphFile(memoryDir, type) {
22546
+ return path45.join(memoryDir, "state", "graphs", `${type}.jsonl`);
22547
+ }
22548
+ function isGraphEdge(raw, expectedType) {
22549
+ if (!raw || typeof raw !== "object") return false;
22550
+ const edge = raw;
22551
+ return edge.type === expectedType && typeof edge.from === "string" && edge.from.length > 0 && typeof edge.to === "string" && edge.to.length > 0 && typeof edge.weight === "number" && Number.isFinite(edge.weight) && typeof edge.label === "string" && typeof edge.ts === "string";
22552
+ }
22553
+ async function graphSnapshotFromMemoryDir(memoryDir) {
22554
+ const nodes = /* @__PURE__ */ new Set();
22555
+ const edges = [];
22556
+ const filesMissing = [];
22557
+ let malformedLines = 0;
22558
+ const seenEdges = /* @__PURE__ */ new Set();
22559
+ for (const type of GRAPH_TYPES) {
22560
+ const filePath = graphFile(memoryDir, type);
22561
+ let raw = "";
22562
+ try {
22563
+ raw = await readFile31(filePath, "utf-8");
22564
+ } catch {
22565
+ filesMissing.push(type);
22566
+ continue;
22567
+ }
22568
+ for (const line of raw.split("\n")) {
22569
+ const trimmed = line.trim();
22570
+ if (!trimmed) continue;
22571
+ let parsed;
22572
+ try {
22573
+ parsed = JSON.parse(trimmed);
22574
+ } catch {
22575
+ malformedLines += 1;
22576
+ continue;
22577
+ }
22578
+ if (!isGraphEdge(parsed, type)) {
22579
+ malformedLines += 1;
22580
+ continue;
22581
+ }
22582
+ const key = graphEdgeKey(parsed);
22583
+ if (seenEdges.has(key)) continue;
22584
+ seenEdges.add(key);
22585
+ edges.push(parsed);
22586
+ nodes.add(parsed.from);
22587
+ nodes.add(parsed.to);
22588
+ }
22589
+ }
22590
+ const sortedEdges = edges.sort(
22591
+ (a, b) => a.type.localeCompare(b.type) || a.from.localeCompare(b.from) || a.to.localeCompare(b.to) || a.ts.localeCompare(b.ts)
22592
+ );
22593
+ const sortedNodes = [...nodes].sort((a, b) => a.localeCompare(b)).map((id) => ({ id }));
22594
+ return {
22595
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22596
+ nodes: sortedNodes,
22597
+ edges: sortedEdges,
22598
+ stats: {
22599
+ nodes: sortedNodes.length,
22600
+ edges: sortedEdges.length,
22601
+ malformedLines,
22602
+ filesMissing
22603
+ }
22604
+ };
22605
+ }
22606
+
22607
+ // src/graph-dashboard-diff.ts
22608
+ function diffGraphSnapshots(previous, next) {
22609
+ const prevNodeSet = new Set(previous.nodes.map((node) => node.id));
22610
+ const nextNodeSet = new Set(next.nodes.map((node) => node.id));
22611
+ const prevEdges = new Map(previous.edges.map((edge) => [graphEdgeKey(edge), edge]));
22612
+ const nextEdges = new Map(next.edges.map((edge) => [graphEdgeKey(edge), edge]));
22613
+ const addedNodes = [...nextNodeSet].filter((id) => !prevNodeSet.has(id)).sort((a, b) => a.localeCompare(b));
22614
+ const removedNodes = [...prevNodeSet].filter((id) => !nextNodeSet.has(id)).sort((a, b) => a.localeCompare(b));
22615
+ const addedEdges = [...nextEdges.entries()].filter(([key]) => !prevEdges.has(key)).map(([, edge]) => edge).sort((a, b) => a.type.localeCompare(b.type) || a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
22616
+ const removedEdges = [...prevEdges.entries()].filter(([key]) => !nextEdges.has(key)).map(([, edge]) => edge).sort((a, b) => a.type.localeCompare(b.type) || a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
22617
+ return {
22618
+ addedNodes,
22619
+ removedNodes,
22620
+ addedEdges,
22621
+ removedEdges
22622
+ };
22623
+ }
22624
+
22625
+ // src/dashboard-runtime.ts
22626
+ function websocketAcceptKey(clientKey) {
22627
+ return createHash9("sha1").update(`${clientKey}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest("base64");
22628
+ }
22629
+ function encodeTextFrame(payload) {
22630
+ const payloadBuffer = Buffer.from(payload, "utf-8");
22631
+ const len = payloadBuffer.length;
22632
+ const header = [129];
22633
+ if (len <= 125) {
22634
+ header.push(len);
22635
+ } else if (len <= 65535) {
22636
+ header.push(126, len >> 8 & 255, len & 255);
22637
+ } else {
22638
+ const high = Math.floor(len / 2 ** 32);
22639
+ const low = len >>> 0;
22640
+ header.push(127, high >> 24 & 255, high >> 16 & 255, high >> 8 & 255, high & 255, low >> 24 & 255, low >> 16 & 255, low >> 8 & 255, low & 255);
22641
+ }
22642
+ return Buffer.concat([Buffer.from(header), payloadBuffer]);
22643
+ }
22644
+ var GraphDashboardServer = class {
22645
+ memoryDir;
22646
+ host;
22647
+ requestedPort;
22648
+ publicDir;
22649
+ watchDebounceMs;
22650
+ server = null;
22651
+ watcher = null;
22652
+ clients = /* @__PURE__ */ new Map();
22653
+ graphSnapshot = {
22654
+ generatedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
22655
+ nodes: [],
22656
+ edges: [],
22657
+ stats: { nodes: 0, edges: 0, malformedLines: 0, filesMissing: [] }
22658
+ };
22659
+ debounceTimer = null;
22660
+ lastError = null;
22661
+ boundPort = 0;
22662
+ constructor(options) {
22663
+ this.memoryDir = options.memoryDir;
22664
+ this.host = options.host?.trim() || "127.0.0.1";
22665
+ this.requestedPort = Number.isFinite(options.port) ? Math.max(0, Math.floor(options.port ?? 0)) : 0;
22666
+ this.publicDir = options.publicDir ?? path46.join(process.cwd(), "dashboard", "public");
22667
+ this.watchDebounceMs = Math.max(50, Math.floor(options.watchDebounceMs ?? 300));
22668
+ }
22669
+ async start() {
22670
+ if (this.server) {
22671
+ return this.status();
22672
+ }
22673
+ await this.rebuildSnapshot();
22674
+ const candidate = createServer2((req, res) => {
22675
+ void this.handleHttp(req, res);
22676
+ });
22677
+ candidate.on("upgrade", (req, socket) => {
22678
+ this.handleUpgrade(req, socket);
22679
+ });
22680
+ try {
22681
+ await new Promise((resolve, reject) => {
22682
+ const onError = (err) => {
22683
+ candidate.off("listening", onListening);
22684
+ reject(err);
22685
+ };
22686
+ const onListening = () => {
22687
+ candidate.off("error", onError);
22688
+ resolve();
22689
+ };
22690
+ candidate.once("error", onError);
22691
+ candidate.once("listening", onListening);
22692
+ candidate.listen(this.requestedPort, this.host);
22693
+ });
22694
+ } catch (err) {
22695
+ this.lastError = err instanceof Error ? err.message : String(err);
22696
+ try {
22697
+ candidate.close();
22698
+ } catch {
22699
+ }
22700
+ throw err;
22701
+ }
22702
+ this.server = candidate;
22703
+ const addr = candidate.address();
22704
+ this.boundPort = typeof addr === "object" && addr ? addr.port : this.requestedPort;
22705
+ this.startWatcher();
22706
+ return this.status();
22707
+ }
22708
+ async stop() {
22709
+ const closeServer = this.server;
22710
+ this.server = null;
22711
+ this.boundPort = 0;
22712
+ if (this.debounceTimer) {
22713
+ clearTimeout(this.debounceTimer);
22714
+ this.debounceTimer = null;
22715
+ }
22716
+ if (this.watcher) {
22717
+ this.watcher.close();
22718
+ this.watcher = null;
22719
+ }
22720
+ for (const client of this.clients.values()) {
22721
+ try {
22722
+ client.socket.destroy();
22723
+ } catch {
22724
+ }
22725
+ }
22726
+ this.clients.clear();
22727
+ if (closeServer) {
22728
+ await new Promise((resolve, reject) => {
22729
+ closeServer.close((err) => {
22730
+ if (err) reject(err);
22731
+ else resolve();
22732
+ });
22733
+ });
22734
+ }
22735
+ }
22736
+ status() {
22737
+ return {
22738
+ running: this.server !== null,
22739
+ host: this.host,
22740
+ port: this.boundPort,
22741
+ watching: this.watcher !== null,
22742
+ lastUpdatedAt: this.graphSnapshot.generatedAt,
22743
+ graphNodeCount: this.graphSnapshot.stats.nodes,
22744
+ graphEdgeCount: this.graphSnapshot.stats.edges
22745
+ };
22746
+ }
22747
+ async handleHttp(req, res) {
22748
+ const url = req.url ?? "/";
22749
+ if (req.method === "GET" && url === "/api/health") {
22750
+ this.respondJson(res, 200, {
22751
+ ok: true,
22752
+ running: this.server !== null,
22753
+ watching: this.watcher !== null,
22754
+ graph: this.graphSnapshot.stats,
22755
+ clients: this.clients.size,
22756
+ lastError: this.lastError ?? void 0
22757
+ });
22758
+ return;
22759
+ }
22760
+ if (req.method === "GET" && url === "/api/graph") {
22761
+ this.respondJson(res, 200, this.graphSnapshot);
22762
+ return;
22763
+ }
22764
+ if (req.method === "GET" && url === "/app.js") {
22765
+ await this.respondStatic(res, path46.join(this.publicDir, "app.js"), "application/javascript; charset=utf-8");
22766
+ return;
22767
+ }
22768
+ if (req.method === "GET" && (url === "/" || url === "/index.html")) {
22769
+ await this.respondStatic(res, path46.join(this.publicDir, "index.html"), "text/html; charset=utf-8");
22770
+ return;
22771
+ }
22772
+ this.respondJson(res, 404, { error: "Not found" });
22773
+ }
22774
+ respondJson(res, status, payload) {
22775
+ const body = JSON.stringify(payload, null, 2);
22776
+ res.statusCode = status;
22777
+ res.setHeader("content-type", "application/json; charset=utf-8");
22778
+ res.setHeader("content-length", String(Buffer.byteLength(body)));
22779
+ res.end(body);
22780
+ }
22781
+ async respondStatic(res, filePath, contentType) {
22782
+ try {
22783
+ const body = await readFile32(filePath, "utf-8");
22784
+ res.statusCode = 200;
22785
+ res.setHeader("content-type", contentType);
22786
+ res.setHeader("content-length", String(Buffer.byteLength(body)));
22787
+ res.end(body);
22788
+ } catch {
22789
+ this.respondJson(res, 404, { error: "Not found" });
22790
+ }
22791
+ }
22792
+ handleUpgrade(req, socket) {
22793
+ const upgrade = typeof req.headers.upgrade === "string" ? req.headers.upgrade.toLowerCase() : "";
22794
+ const key = req.headers["sec-websocket-key"];
22795
+ if (upgrade !== "websocket" || typeof key !== "string") {
22796
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
22797
+ socket.destroy();
22798
+ return;
22799
+ }
22800
+ const accept = websocketAcceptKey(key);
22801
+ socket.write(
22802
+ [
22803
+ "HTTP/1.1 101 Switching Protocols",
22804
+ "Upgrade: websocket",
22805
+ "Connection: Upgrade",
22806
+ `Sec-WebSocket-Accept: ${accept}`,
22807
+ "",
22808
+ ""
22809
+ ].join("\r\n")
22810
+ );
22811
+ const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
22812
+ this.clients.set(id, { id, socket });
22813
+ socket.on("close", () => {
22814
+ this.clients.delete(id);
22815
+ });
22816
+ socket.on("error", () => {
22817
+ this.clients.delete(id);
22818
+ });
22819
+ const hello = JSON.stringify({
22820
+ type: "hello",
22821
+ graph: this.graphSnapshot
22822
+ });
22823
+ socket.write(encodeTextFrame(hello));
22824
+ }
22825
+ broadcast(payload) {
22826
+ const frame = encodeTextFrame(JSON.stringify(payload));
22827
+ for (const [id, client] of this.clients.entries()) {
22828
+ try {
22829
+ client.socket.write(frame);
22830
+ } catch {
22831
+ this.clients.delete(id);
22832
+ }
22833
+ }
22834
+ }
22835
+ startWatcher() {
22836
+ const graphDir = path46.join(this.memoryDir, "state", "graphs");
22837
+ try {
22838
+ this.watcher = watch(graphDir, { persistent: false }, () => {
22839
+ this.scheduleRebuild();
22840
+ });
22841
+ this.watcher.on("error", (err) => {
22842
+ this.lastError = err instanceof Error ? err.message : String(err);
22843
+ });
22844
+ } catch (err) {
22845
+ this.lastError = err instanceof Error ? err.message : String(err);
22846
+ this.watcher = null;
22847
+ }
22848
+ }
22849
+ scheduleRebuild() {
22850
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
22851
+ this.debounceTimer = setTimeout(() => {
22852
+ this.debounceTimer = null;
22853
+ void this.rebuildAndBroadcast();
22854
+ }, this.watchDebounceMs);
22855
+ }
22856
+ async rebuildAndBroadcast() {
22857
+ const previous = this.graphSnapshot;
22858
+ await this.rebuildSnapshot();
22859
+ const patch = diffGraphSnapshots(previous, this.graphSnapshot);
22860
+ if (patch.addedEdges.length === 0 && patch.removedEdges.length === 0 && patch.addedNodes.length === 0 && patch.removedNodes.length === 0) {
22861
+ return;
22862
+ }
22863
+ this.broadcast({
22864
+ type: "graph_patch",
22865
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22866
+ patch,
22867
+ graph: this.graphSnapshot
22868
+ });
22869
+ }
22870
+ async rebuildSnapshot() {
22871
+ try {
22872
+ this.graphSnapshot = await graphSnapshotFromMemoryDir(this.memoryDir);
22873
+ this.lastError = null;
22874
+ } catch (err) {
22875
+ this.lastError = err instanceof Error ? err.message : String(err);
22876
+ }
22877
+ }
22878
+ };
22879
+
22101
22880
  // src/compat/checks.ts
22102
- import { access as access2, readFile as readFile30 } from "fs/promises";
22103
- import path44 from "path";
22881
+ import { access as access2, readFile as readFile33 } from "fs/promises";
22882
+ import path47 from "path";
22104
22883
  import { spawn as spawn4 } from "child_process";
22105
22884
  var REQUIRED_HOOKS = ["before_agent_start", "agent_end"];
22106
22885
  function isSafeCommandToken(command) {
@@ -22282,13 +23061,13 @@ function compareVersions(a, b) {
22282
23061
  async function runCompatChecks(options) {
22283
23062
  const checks = [];
22284
23063
  const runner = options.runner ?? defaultRunner;
22285
- const pluginJsonPath = path44.join(options.repoRoot, "openclaw.plugin.json");
22286
- const packageJsonPath = path44.join(options.repoRoot, "package.json");
22287
- const indexPath = path44.join(options.repoRoot, "src", "index.ts");
23064
+ const pluginJsonPath = path47.join(options.repoRoot, "openclaw.plugin.json");
23065
+ const packageJsonPath = path47.join(options.repoRoot, "package.json");
23066
+ const indexPath = path47.join(options.repoRoot, "src", "index.ts");
22288
23067
  let pluginRaw = "";
22289
23068
  let pluginManifestPresent = false;
22290
23069
  try {
22291
- pluginRaw = await readFile30(pluginJsonPath, "utf-8");
23070
+ pluginRaw = await readFile33(pluginJsonPath, "utf-8");
22292
23071
  pluginManifestPresent = true;
22293
23072
  checks.push({
22294
23073
  id: "plugin-manifest-present",
@@ -22337,7 +23116,7 @@ async function runCompatChecks(options) {
22337
23116
  let packageRaw = "";
22338
23117
  let packageJsonPresent = false;
22339
23118
  try {
22340
- packageRaw = await readFile30(packageJsonPath, "utf-8");
23119
+ packageRaw = await readFile33(packageJsonPath, "utf-8");
22341
23120
  packageJsonPresent = true;
22342
23121
  } catch {
22343
23122
  checks.push({
@@ -22408,7 +23187,7 @@ async function runCompatChecks(options) {
22408
23187
  }
22409
23188
  try {
22410
23189
  await access2(indexPath);
22411
- const indexRaw = await readFile30(indexPath, "utf-8");
23190
+ const indexRaw = await readFile33(indexPath, "utf-8");
22412
23191
  const structuralSource = stripCommentsAndStrings(indexRaw);
22413
23192
  const hooks = parseHookRegistrations(indexRaw);
22414
23193
  const missingHooks = REQUIRED_HOOKS.filter((hook) => !hooks.has(hook));
@@ -22520,6 +23299,8 @@ function planAggressiveDuplicateDeletions(memories) {
22520
23299
  }
22521
23300
  var activeWebDavServer = null;
22522
23301
  var webDavOperationChain = Promise.resolve();
23302
+ var activeDashboardServer = null;
23303
+ var dashboardOperationChain = Promise.resolve();
22523
23304
  async function withWebDavLock(operation) {
22524
23305
  const run = webDavOperationChain.then(operation, operation);
22525
23306
  webDavOperationChain = run.then(
@@ -22528,6 +23309,14 @@ async function withWebDavLock(operation) {
22528
23309
  );
22529
23310
  return run;
22530
23311
  }
23312
+ async function withDashboardLock(operation) {
23313
+ const run = dashboardOperationChain.then(operation, operation);
23314
+ dashboardOperationChain = run.then(
23315
+ () => void 0,
23316
+ () => void 0
23317
+ );
23318
+ return run;
23319
+ }
22531
23320
  function isRoutePatternType(value) {
22532
23321
  return value === "keyword" || value === "regex";
22533
23322
  }
@@ -22615,6 +23404,21 @@ async function runGraphHealthCliCommand(options) {
22615
23404
  includeRepairGuidance: options.includeRepairGuidance
22616
23405
  });
22617
23406
  }
23407
+ async function runSessionCheckCliCommand(options) {
23408
+ return analyzeSessionIntegrity({ memoryDir: options.memoryDir });
23409
+ }
23410
+ async function runSessionRepairCliCommand(options) {
23411
+ const report = await analyzeSessionIntegrity({ memoryDir: options.memoryDir });
23412
+ const dryRun = options.apply !== true || options.dryRun === true;
23413
+ const plan = planSessionRepair({
23414
+ report,
23415
+ dryRun,
23416
+ allowSessionFileRepair: options.allowSessionFileRepair === true,
23417
+ sessionFilesDir: options.sessionFilesDir
23418
+ });
23419
+ const applyResult = await applySessionRepair({ plan });
23420
+ return { report, plan, applyResult };
23421
+ }
22618
23422
  async function runTierStatusCliCommand(orchestrator) {
22619
23423
  return orchestrator.getTierMigrationStatus();
22620
23424
  }
@@ -22824,10 +23628,10 @@ function effectivePolicyValuesForVersion(values, config) {
22824
23628
  }
22825
23629
  function policyVersionForValues(values, config) {
22826
23630
  const normalized = effectivePolicyValuesForVersion(values, config);
22827
- return createHash9("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
23631
+ return createHash10("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
22828
23632
  }
22829
23633
  async function readRuntimePolicySnapshot2(config, fileName) {
22830
- const filePath = path45.join(config.memoryDir, "state", fileName);
23634
+ const filePath = path48.join(config.memoryDir, "state", fileName);
22831
23635
  const snapshot = await readRuntimePolicySnapshot(filePath, {
22832
23636
  maxStaleDecayThreshold: config.lifecycleArchiveDecayThreshold
22833
23637
  });
@@ -23064,8 +23868,8 @@ async function runWebDavServeCliCommand(options) {
23064
23868
  const current = activeWebDavServer.status();
23065
23869
  if (current.running) return current;
23066
23870
  }
23067
- const createServer2 = options.createServer ?? WebDavServer.create;
23068
- const server = await createServer2({
23871
+ const createServer3 = options.createServer ?? WebDavServer.create;
23872
+ const server = await createServer3({
23069
23873
  enabled: options.enabled ?? true,
23070
23874
  host: options.host,
23071
23875
  port: options.port ?? 8080,
@@ -23096,6 +23900,47 @@ async function runWebDavStopCliCommand() {
23096
23900
  return { stopped: true };
23097
23901
  });
23098
23902
  }
23903
+ async function runDashboardStartCliCommand(options) {
23904
+ return withDashboardLock(async () => {
23905
+ if (activeDashboardServer) {
23906
+ const status = activeDashboardServer.status();
23907
+ if (status.running) return status;
23908
+ }
23909
+ const createServer3 = options.createServer ?? ((opts) => new GraphDashboardServer({
23910
+ memoryDir: opts.memoryDir,
23911
+ host: opts.host,
23912
+ port: opts.port,
23913
+ publicDir: opts.publicDir
23914
+ }));
23915
+ const server = createServer3(options);
23916
+ activeDashboardServer = server;
23917
+ try {
23918
+ return await server.start();
23919
+ } catch (err) {
23920
+ if (activeDashboardServer === server) {
23921
+ activeDashboardServer = null;
23922
+ }
23923
+ throw err;
23924
+ }
23925
+ });
23926
+ }
23927
+ async function runDashboardStopCliCommand() {
23928
+ return withDashboardLock(async () => {
23929
+ if (!activeDashboardServer) return { stopped: false };
23930
+ const server = activeDashboardServer;
23931
+ await server.stop();
23932
+ if (activeDashboardServer === server) {
23933
+ activeDashboardServer = null;
23934
+ }
23935
+ return { stopped: true };
23936
+ });
23937
+ }
23938
+ async function runDashboardStatusCliCommand() {
23939
+ return withDashboardLock(async () => {
23940
+ if (!activeDashboardServer) return { running: false };
23941
+ return activeDashboardServer.status();
23942
+ });
23943
+ }
23099
23944
  async function runCompatCliCommand(options = {}) {
23100
23945
  const report = await runCompatChecks({
23101
23946
  repoRoot: options.repoRoot ?? process.cwd(),
@@ -23286,7 +24131,7 @@ async function withTimeout(promise, timeoutMs, timeoutMessage) {
23286
24131
  }
23287
24132
  async function runReplayCliCommand(orchestrator, options) {
23288
24133
  const extractionIdleTimeoutMs = Number.isFinite(options.extractionIdleTimeoutMs) ? Math.max(1e3, Math.floor(options.extractionIdleTimeoutMs)) : 15 * 6e4;
23289
- const inputRaw = await readFile31(options.inputPath, "utf-8");
24134
+ const inputRaw = await readFile34(options.inputPath, "utf-8");
23290
24135
  const registry = buildReplayNormalizerRegistry([
23291
24136
  openclawReplayNormalizer,
23292
24137
  claudeReplayNormalizer,
@@ -23351,7 +24196,7 @@ async function runReplayCliCommand(orchestrator, options) {
23351
24196
  async function getPluginVersion() {
23352
24197
  try {
23353
24198
  const pkgPath = new URL("../package.json", import.meta.url);
23354
- const raw = await readFile31(pkgPath, "utf-8");
24199
+ const raw = await readFile34(pkgPath, "utf-8");
23355
24200
  const parsed = JSON.parse(raw);
23356
24201
  return parsed.version ?? "unknown";
23357
24202
  } catch {
@@ -23370,32 +24215,32 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
23370
24215
  const ns = (namespace ?? "").trim();
23371
24216
  if (!ns) return orchestrator.config.memoryDir;
23372
24217
  if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
23373
- const candidate = path45.join(orchestrator.config.memoryDir, "namespaces", ns);
24218
+ const candidate = path48.join(orchestrator.config.memoryDir, "namespaces", ns);
23374
24219
  if (ns === orchestrator.config.defaultNamespace) {
23375
24220
  return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
23376
24221
  }
23377
24222
  return candidate;
23378
24223
  }
23379
24224
  async function readAllMemoryFiles(memoryDir) {
23380
- const roots = [path45.join(memoryDir, "facts"), path45.join(memoryDir, "corrections")];
24225
+ const roots = [path48.join(memoryDir, "facts"), path48.join(memoryDir, "corrections")];
23381
24226
  const out = [];
23382
24227
  const walk = async (dir) => {
23383
24228
  let entries;
23384
24229
  try {
23385
- entries = await readdir19(dir, { withFileTypes: true });
24230
+ entries = await readdir20(dir, { withFileTypes: true });
23386
24231
  } catch {
23387
24232
  return;
23388
24233
  }
23389
24234
  for (const entry of entries) {
23390
24235
  const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
23391
- const fullPath = path45.join(dir, entryName);
24236
+ const fullPath = path48.join(dir, entryName);
23392
24237
  if (entry.isDirectory()) {
23393
24238
  await walk(fullPath);
23394
24239
  continue;
23395
24240
  }
23396
24241
  if (!entry.isFile() || !entryName.endsWith(".md")) continue;
23397
24242
  try {
23398
- const raw = await readFile31(fullPath, "utf-8");
24243
+ const raw = await readFile34(fullPath, "utf-8");
23399
24244
  const parsed = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
23400
24245
  if (!parsed) continue;
23401
24246
  const fmRaw = parsed[1];
@@ -23673,6 +24518,25 @@ function registerCli(api, orchestrator) {
23673
24518
  console.log(JSON.stringify(report, null, 2));
23674
24519
  console.log("OK");
23675
24520
  });
24521
+ cmd.command("session-check").description("Analyze transcript/checkpoint continuity integrity without mutating files").action(async () => {
24522
+ const report = await runSessionCheckCliCommand({
24523
+ memoryDir: orchestrator.config.memoryDir
24524
+ });
24525
+ console.log(JSON.stringify(report, null, 2));
24526
+ console.log("OK");
24527
+ });
24528
+ cmd.command("session-repair").description("Generate/apply bounded Engram session integrity repairs (dry-run by default)").option("--apply", "Apply repairs (default: dry-run)").option("--dry-run", "Force dry-run output").option("--allow-session-file-repair", "Allow explicit OpenClaw session-file repair path (still no automatic rewiring)").option("--session-files-dir <path>", "Optional OpenClaw session files directory for guarded repair workflow").action(async (...args) => {
24529
+ const options = args[0] ?? {};
24530
+ const result = await runSessionRepairCliCommand({
24531
+ memoryDir: orchestrator.config.memoryDir,
24532
+ apply: options.apply === true,
24533
+ dryRun: options.dryRun === true,
24534
+ allowSessionFileRepair: options.allowSessionFileRepair === true,
24535
+ sessionFilesDir: typeof options.sessionFilesDir === "string" && options.sessionFilesDir.trim().length > 0 ? options.sessionFilesDir.trim() : void 0
24536
+ });
24537
+ console.log(JSON.stringify(result, null, 2));
24538
+ console.log("OK");
24539
+ });
23676
24540
  cmd.command("tier-status").description("Show tier migration telemetry and last-cycle summary").action(async () => {
23677
24541
  const status = await runTierStatusCliCommand(orchestrator);
23678
24542
  console.log(JSON.stringify(status, null, 2));
@@ -23811,6 +24675,29 @@ function registerCli(api, orchestrator) {
23811
24675
  console.log(JSON.stringify(result, null, 2));
23812
24676
  console.log("OK");
23813
24677
  });
24678
+ const dashboardCmd = cmd.command("dashboard").description("Manage live graph dashboard service");
24679
+ dashboardCmd.command("start").description("Start dashboard server (localhost by default)").option("--host <host>", "Bind host", "127.0.0.1").option("--port <n>", "Bind port", "4319").option("--public-dir <path>", "Override static dashboard assets path").action(async (...args) => {
24680
+ const options = args[0] ?? {};
24681
+ const portRaw = parseInt(String(options.port ?? "4319"), 10);
24682
+ const status = await runDashboardStartCliCommand({
24683
+ memoryDir: orchestrator.config.memoryDir,
24684
+ host: typeof options.host === "string" ? options.host : "127.0.0.1",
24685
+ port: Number.isFinite(portRaw) ? portRaw : 4319,
24686
+ publicDir: typeof options.publicDir === "string" ? options.publicDir : void 0
24687
+ });
24688
+ console.log(JSON.stringify(status, null, 2));
24689
+ console.log("OK");
24690
+ });
24691
+ dashboardCmd.command("stop").description("Stop dashboard server").action(async () => {
24692
+ const result = await runDashboardStopCliCommand();
24693
+ console.log(JSON.stringify(result, null, 2));
24694
+ console.log("OK");
24695
+ });
24696
+ dashboardCmd.command("status").description("Show dashboard server status").action(async () => {
24697
+ const status = await runDashboardStatusCliCommand();
24698
+ console.log(JSON.stringify(status, null, 2));
24699
+ console.log("OK");
24700
+ });
23814
24701
  const routeCmd = cmd.command("route").description("Manage custom memory routing rules");
23815
24702
  routeCmd.command("list").description("List configured routing rules").action(async () => {
23816
24703
  const rules = await runRouteCliCommand({
@@ -24051,7 +24938,7 @@ function registerCli(api, orchestrator) {
24051
24938
  let deleted = 0;
24052
24939
  for (const filePath of plan.deletePaths) {
24053
24940
  try {
24054
- await unlink5(filePath);
24941
+ await unlink6(filePath);
24055
24942
  deleted += 1;
24056
24943
  } catch (err) {
24057
24944
  console.log(` failed to delete ${filePath}: ${String(err)}`);
@@ -24099,7 +24986,7 @@ function registerCli(api, orchestrator) {
24099
24986
  let deleted = 0;
24100
24987
  for (const filePath of plan.deletePaths) {
24101
24988
  try {
24102
- await unlink5(filePath);
24989
+ await unlink6(filePath);
24103
24990
  deleted += 1;
24104
24991
  } catch (err) {
24105
24992
  console.log(` failed to delete ${filePath}: ${String(err)}`);
@@ -24263,7 +25150,7 @@ function registerCli(api, orchestrator) {
24263
25150
  }
24264
25151
  });
24265
25152
  cmd.command("identity").description("Show agent identity reflections").action(async () => {
24266
- const workspaceDir = path45.join(process.env.HOME ?? "~", ".openclaw", "workspace");
25153
+ const workspaceDir = path48.join(process.env.HOME ?? "~", ".openclaw", "workspace");
24267
25154
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
24268
25155
  if (!identity) {
24269
25156
  console.log("No identity file found.");
@@ -24486,8 +25373,8 @@ function registerCli(api, orchestrator) {
24486
25373
  const options = args[0] ?? {};
24487
25374
  const threadId = options.thread;
24488
25375
  const top = parseInt(options.top ?? "10", 10);
24489
- const memoryDir = path45.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
24490
- const threading = new ThreadingManager(path45.join(memoryDir, "threads"));
25376
+ const memoryDir = path48.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
25377
+ const threading = new ThreadingManager(path48.join(memoryDir, "threads"));
24491
25378
  if (threadId) {
24492
25379
  const thread = await threading.loadThread(threadId);
24493
25380
  if (!thread) {
@@ -24660,16 +25547,16 @@ function parseDuration(duration) {
24660
25547
  }
24661
25548
 
24662
25549
  // src/index.ts
24663
- import { readFile as readFile32, writeFile as writeFile27 } from "fs/promises";
25550
+ import { readFile as readFile35, writeFile as writeFile28 } from "fs/promises";
24664
25551
  import { readFileSync as readFileSync4 } from "fs";
24665
- import path46 from "path";
25552
+ import path49 from "path";
24666
25553
  import os5 from "os";
24667
25554
  var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
24668
25555
  function loadPluginConfigFromFile() {
24669
25556
  try {
24670
25557
  const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
24671
25558
  const homeDir = process.env.HOME ?? os5.homedir();
24672
- const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path46.join(homeDir, ".openclaw", "openclaw.json");
25559
+ const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path49.join(homeDir, ".openclaw", "openclaw.json");
24673
25560
  const content = readFileSync4(configPath, "utf-8");
24674
25561
  const config = JSON.parse(content);
24675
25562
  const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
@@ -24877,11 +25764,11 @@ Use this context naturally when relevant. Never quote or expose this memory cont
24877
25764
  );
24878
25765
  async function ensureHourlySummaryCron(api2) {
24879
25766
  const jobId = "engram-hourly-summary";
24880
- const cronFilePath = path46.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
25767
+ const cronFilePath = path49.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
24881
25768
  try {
24882
25769
  let jobsData = { version: 1, jobs: [] };
24883
25770
  try {
24884
- const content = await readFile32(cronFilePath, "utf-8");
25771
+ const content = await readFile35(cronFilePath, "utf-8");
24885
25772
  jobsData = JSON.parse(content);
24886
25773
  } catch {
24887
25774
  }
@@ -24918,7 +25805,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
24918
25805
  state: {}
24919
25806
  };
24920
25807
  jobsData.jobs.push(newJob);
24921
- await writeFile27(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
25808
+ await writeFile28(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
24922
25809
  log.info("auto-registered hourly summary cron job");
24923
25810
  } catch (err) {
24924
25811
  log.error("failed to auto-register hourly summary cron job:", err);