@joshuaswarren/openclaw-engram 8.3.79 → 8.3.81

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
@@ -450,9 +450,9 @@ function parseConfig(raw) {
450
450
  }
451
451
 
452
452
  // src/orchestrator.ts
453
- import path26 from "path";
453
+ import path27 from "path";
454
454
  import { createHash as createHash6 } from "crypto";
455
- import { mkdir as mkdir19, readdir as readdir11, readFile as readFile18, writeFile as writeFile17 } from "fs/promises";
455
+ import { mkdir as mkdir20, readdir as readdir11, readFile as readFile19, writeFile as writeFile18 } from "fs/promises";
456
456
 
457
457
  // src/signal.ts
458
458
  var BUILTIN_HIGH_PATTERNS = [
@@ -8665,6 +8665,192 @@ ${truncatedConversation}`;
8665
8665
  }
8666
8666
  };
8667
8667
 
8668
+ // src/lifecycle.ts
8669
+ var DEFAULT_POLICY = {
8670
+ promoteHeatThreshold: 0.55,
8671
+ staleDecayThreshold: 0.65,
8672
+ archiveDecayThreshold: 0.85,
8673
+ protectedCategories: ["decision", "principle", "commitment", "preference"]
8674
+ };
8675
+ function clamp01(value) {
8676
+ if (!Number.isFinite(value)) return 0;
8677
+ if (value < 0) return 0;
8678
+ if (value > 1) return 1;
8679
+ return value;
8680
+ }
8681
+ function clampLifecycleThreshold(value) {
8682
+ return clamp01(value);
8683
+ }
8684
+ function parseIsoMs(value) {
8685
+ if (!value) return null;
8686
+ const ms = Date.parse(value);
8687
+ return Number.isFinite(ms) ? ms : null;
8688
+ }
8689
+ function daysSince(value, nowMs) {
8690
+ const ts = parseIsoMs(value);
8691
+ if (ts === null) return 365;
8692
+ return Math.max(0, (nowMs - ts) / 864e5);
8693
+ }
8694
+ function confidenceTierWeight(frontmatter) {
8695
+ switch (frontmatter.confidenceTier) {
8696
+ case "explicit":
8697
+ return 1;
8698
+ case "implied":
8699
+ return 0.8;
8700
+ case "inferred":
8701
+ return 0.6;
8702
+ case "speculative":
8703
+ return 0.35;
8704
+ default:
8705
+ return clamp01(frontmatter.confidence ?? 0.5);
8706
+ }
8707
+ }
8708
+ function accessWeight(accessCount) {
8709
+ const raw = accessCount ?? 0;
8710
+ if (raw <= 0) return 0;
8711
+ return clamp01(Math.log1p(raw) / Math.log1p(20));
8712
+ }
8713
+ function recencyWeight(frontmatter, nowMs) {
8714
+ const lastTouch = frontmatter.lastAccessed ?? frontmatter.updated ?? frontmatter.created;
8715
+ const ageDays = daysSince(lastTouch, nowMs);
8716
+ return clamp01(1 - ageDays / 90);
8717
+ }
8718
+ function feedbackWeight(signals) {
8719
+ const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);
8720
+ return clamp01((raw + 1) / 2);
8721
+ }
8722
+ function boundedFeedbackScore(signals) {
8723
+ const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);
8724
+ if (!Number.isFinite(raw)) return 0;
8725
+ if (raw < -1) return -1;
8726
+ if (raw > 1) return 1;
8727
+ return raw;
8728
+ }
8729
+ function isProtectedMemory(frontmatter, policy) {
8730
+ return frontmatter.policyClass === "protected" || policy.protectedCategories.includes(frontmatter.category);
8731
+ }
8732
+ function resolveLifecycleState(frontmatter) {
8733
+ if (frontmatter.status === "archived") return "archived";
8734
+ return frontmatter.lifecycleState ?? "candidate";
8735
+ }
8736
+ function computeHeat(memory, now, signals) {
8737
+ const frontmatter = memory.frontmatter;
8738
+ if (frontmatter.status === "archived") return 0;
8739
+ const inputs = computeLifecycleValueInputs(memory, now, signals);
8740
+ const score = inputs.confidence * 0.25 + inputs.access * 0.3 + inputs.recency * 0.2 + inputs.importance * 0.15 + inputs.feedback * 0.1 - inputs.disputedPenalty;
8741
+ return clamp01(score);
8742
+ }
8743
+ function computeLifecycleValueInputs(memory, now, signals) {
8744
+ const frontmatter = memory.frontmatter;
8745
+ const nowMs = now.getTime();
8746
+ return {
8747
+ confidence: confidenceTierWeight(frontmatter),
8748
+ access: accessWeight(frontmatter.accessCount),
8749
+ recency: recencyWeight(frontmatter, nowMs),
8750
+ importance: clamp01(frontmatter.importance?.score ?? 0.5),
8751
+ feedback: feedbackWeight(signals),
8752
+ disputedPenalty: frontmatter.verificationState === "disputed" ? 0.2 : 0
8753
+ };
8754
+ }
8755
+ function computeDecay(memory, now, signals) {
8756
+ const frontmatter = memory.frontmatter;
8757
+ if (frontmatter.status === "archived") return 1;
8758
+ const nowMs = now.getTime();
8759
+ const ageDays = daysSince(frontmatter.updated ?? frontmatter.created, nowMs);
8760
+ const staleAccessDays = daysSince(frontmatter.lastAccessed, nowMs);
8761
+ const ageRisk = clamp01(ageDays / 180);
8762
+ const staleAccessRisk = clamp01(staleAccessDays / 120);
8763
+ const confidenceRisk = 1 - confidenceTierWeight(frontmatter);
8764
+ const feedbackRisk = clamp01((boundedFeedbackScore(signals) * -1 + 1) / 2);
8765
+ const heat = computeHeat(memory, now, signals);
8766
+ const score = ageRisk * 0.3 + staleAccessRisk * 0.25 + confidenceRisk * 0.2 + feedbackRisk * 0.1 + (1 - heat) * 0.15;
8767
+ return clamp01(score);
8768
+ }
8769
+ function toTerminalDisputedState(currentState) {
8770
+ if (currentState === "archived") return "archived";
8771
+ return "stale";
8772
+ }
8773
+ function isActiveEligible(verificationState) {
8774
+ return verificationState === "user_confirmed" || verificationState === "system_inferred";
8775
+ }
8776
+ function decideLifecycleTransition(memory, policy, now, signals) {
8777
+ const mergedPolicy = { ...DEFAULT_POLICY, ...policy };
8778
+ const frontmatter = memory.frontmatter;
8779
+ const currentState = resolveLifecycleState(frontmatter);
8780
+ const heatScore = computeHeat(memory, now, signals);
8781
+ const decayScore = computeDecay(memory, now, signals);
8782
+ const protectedMemory = isProtectedMemory(frontmatter, mergedPolicy);
8783
+ if (currentState === "archived") {
8784
+ return {
8785
+ currentState,
8786
+ nextState: "archived",
8787
+ heatScore,
8788
+ decayScore,
8789
+ changed: false,
8790
+ reason: "archived_is_terminal"
8791
+ };
8792
+ }
8793
+ if (frontmatter.verificationState === "disputed") {
8794
+ const nextState = toTerminalDisputedState(currentState);
8795
+ return {
8796
+ currentState,
8797
+ nextState,
8798
+ heatScore,
8799
+ decayScore,
8800
+ changed: nextState !== currentState,
8801
+ reason: "disputed_memories_do_not_promote_to_active"
8802
+ };
8803
+ }
8804
+ if (decayScore >= mergedPolicy.archiveDecayThreshold && !protectedMemory) {
8805
+ return {
8806
+ currentState,
8807
+ nextState: "archived",
8808
+ heatScore,
8809
+ decayScore,
8810
+ changed: true,
8811
+ reason: "decay_exceeded_archive_threshold"
8812
+ };
8813
+ }
8814
+ if (decayScore >= mergedPolicy.staleDecayThreshold) {
8815
+ return {
8816
+ currentState,
8817
+ nextState: "stale",
8818
+ heatScore,
8819
+ decayScore,
8820
+ changed: currentState !== "stale",
8821
+ reason: "decay_exceeded_stale_threshold"
8822
+ };
8823
+ }
8824
+ if (heatScore >= mergedPolicy.promoteHeatThreshold) {
8825
+ const nextState = isActiveEligible(frontmatter.verificationState) ? "active" : "validated";
8826
+ return {
8827
+ currentState,
8828
+ nextState,
8829
+ heatScore,
8830
+ decayScore,
8831
+ changed: currentState !== nextState,
8832
+ reason: "heat_exceeded_promote_threshold"
8833
+ };
8834
+ }
8835
+ return {
8836
+ currentState,
8837
+ nextState: currentState,
8838
+ heatScore,
8839
+ decayScore,
8840
+ changed: false,
8841
+ reason: "no_transition"
8842
+ };
8843
+ }
8844
+
8845
+ // src/retrieval.ts
8846
+ function applyRuntimeRetrievalPolicy(base, runtime) {
8847
+ const fromRuntime = runtime?.recencyWeight;
8848
+ if (typeof fromRuntime !== "number") {
8849
+ return { recencyWeight: clamp01(base.recencyWeight) };
8850
+ }
8851
+ return { recencyWeight: clamp01(fromRuntime) };
8852
+ }
8853
+
8668
8854
  // src/rerank.ts
8669
8855
  var RerankCache = class {
8670
8856
  entries = /* @__PURE__ */ new Map();
@@ -8951,6 +9137,7 @@ var LastRecallStore = class {
8951
9137
  queryHash,
8952
9138
  queryLen: opts.query.length,
8953
9139
  memoryIds: opts.memoryIds,
9140
+ policyVersion: opts.policyVersion,
8954
9141
  identityInjectionMode: opts.identityInjection?.mode,
8955
9142
  identityInjectedChars: opts.identityInjection?.injectedChars,
8956
9143
  identityInjectionTruncated: opts.identityInjection?.truncated
@@ -9049,18 +9236,18 @@ function sanitizeNonNegativeInt(value) {
9049
9236
  if (!Number.isFinite(value)) return 0;
9050
9237
  return Math.max(0, Math.floor(value));
9051
9238
  }
9052
- function parseIsoMs(value) {
9239
+ function parseIsoMs2(value) {
9053
9240
  if (!value) return 0;
9054
9241
  const ms = Date.parse(value);
9055
9242
  return Number.isFinite(ms) ? ms : 0;
9056
9243
  }
9057
9244
  function mergeSessionCursor(existing, incoming) {
9058
- const existingObservedMs = parseIsoMs(existing.lastObservedAt);
9059
- const incomingObservedMs = parseIsoMs(incoming.lastObservedAt);
9060
- const existingTriggeredMs = parseIsoMs(existing.lastTriggeredAt);
9061
- const incomingTriggeredMs = parseIsoMs(incoming.lastTriggeredAt);
9062
- const existingResetMs = parseIsoMs(existing.lastResetAt);
9063
- const incomingResetMs = parseIsoMs(incoming.lastResetAt);
9245
+ const existingObservedMs = parseIsoMs2(existing.lastObservedAt);
9246
+ const incomingObservedMs = parseIsoMs2(incoming.lastObservedAt);
9247
+ const existingTriggeredMs = parseIsoMs2(existing.lastTriggeredAt);
9248
+ const incomingTriggeredMs = parseIsoMs2(incoming.lastTriggeredAt);
9249
+ const existingResetMs = parseIsoMs2(existing.lastResetAt);
9250
+ const incomingResetMs = parseIsoMs2(incoming.lastResetAt);
9064
9251
  const observedAt = incomingObservedMs >= existingObservedMs ? incoming.lastObservedAt : existing.lastObservedAt;
9065
9252
  const triggeredAt = incomingTriggeredMs >= existingTriggeredMs ? incoming.lastTriggeredAt : existing.lastTriggeredAt;
9066
9253
  const incomingIsNewer = incomingObservedMs >= existingObservedMs;
@@ -10815,238 +11002,64 @@ var TmtBuilder = class {
10815
11002
  if (!countMatch || parseInt(countMatch[1], 10) !== totalCount) {
10816
11003
  shouldBuild = true;
10817
11004
  }
10818
- } catch {
10819
- shouldBuild = true;
10820
- }
10821
- }
10822
- if (!shouldBuild) return;
10823
- const summary = await summarize2(capTmtSummaryInputs(weekSummaries, "persona"), "persona");
10824
- const now = (/* @__PURE__ */ new Date()).toISOString();
10825
- const fm = {
10826
- level: "persona",
10827
- periodStart: earliestStart ?? now,
10828
- periodEnd: latestEnd ?? now,
10829
- memoryCount: totalCount,
10830
- sourceIds: [],
10831
- builtAt: now
10832
- };
10833
- await writeFile12(nodePath, serialiseTmtNode(fm, summary), "utf8");
10834
- } catch (err) {
10835
- console.warn(`[engram] tmt: persona node build failed (ignored): ${err}`);
10836
- }
10837
- }
10838
- /**
10839
- * Return the summary text of the most relevant TMT node for a given prompt.
10840
- * Preference: day node for today > most recent day node.
10841
- * Returns null if no nodes exist or feature is disabled.
10842
- */
10843
- async getMostRelevantNode() {
10844
- if (!this.cfg.temporalMemoryTreeEnabled) return null;
10845
- try {
10846
- const dir = tmtDir(this.baseDir);
10847
- if (!fs.existsSync(dir)) return null;
10848
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
10849
- const todayDay = dayNodePath(this.baseDir, today);
10850
- if (fs.existsSync(todayDay)) {
10851
- const content = await readFile13(todayDay, "utf8");
10852
- const summary = content.replace(/^---[\s\S]*?---\n\n?/, "").trim();
10853
- if (summary) return { level: "day", summary };
10854
- }
10855
- let entries = [];
10856
- try {
10857
- entries = await readdir7(dir);
10858
- } catch {
10859
- return null;
10860
- }
10861
- const dateDirs = entries.filter((e) => /^\d{4}-\d{2}-\d{2}$/.test(e)).sort().reverse();
10862
- for (const dateDir of dateDirs) {
10863
- const dayPath = dayNodePath(this.baseDir, dateDir);
10864
- if (fs.existsSync(dayPath)) {
10865
- const content = await readFile13(dayPath, "utf8");
10866
- const summary = content.replace(/^---[\s\S]*?---\n\n?/, "").trim();
10867
- if (summary) return { level: "day", summary };
10868
- }
10869
- }
10870
- return null;
10871
- } catch {
10872
- return null;
10873
- }
10874
- }
10875
- };
10876
-
10877
- // src/lifecycle.ts
10878
- var DEFAULT_POLICY = {
10879
- promoteHeatThreshold: 0.55,
10880
- staleDecayThreshold: 0.65,
10881
- archiveDecayThreshold: 0.85,
10882
- protectedCategories: ["decision", "principle", "commitment", "preference"]
10883
- };
10884
- function clamp01(value) {
10885
- if (!Number.isFinite(value)) return 0;
10886
- if (value < 0) return 0;
10887
- if (value > 1) return 1;
10888
- return value;
10889
- }
10890
- function parseIsoMs2(value) {
10891
- if (!value) return null;
10892
- const ms = Date.parse(value);
10893
- return Number.isFinite(ms) ? ms : null;
10894
- }
10895
- function daysSince(value, nowMs) {
10896
- const ts = parseIsoMs2(value);
10897
- if (ts === null) return 365;
10898
- return Math.max(0, (nowMs - ts) / 864e5);
10899
- }
10900
- function confidenceTierWeight(frontmatter) {
10901
- switch (frontmatter.confidenceTier) {
10902
- case "explicit":
10903
- return 1;
10904
- case "implied":
10905
- return 0.8;
10906
- case "inferred":
10907
- return 0.6;
10908
- case "speculative":
10909
- return 0.35;
10910
- default:
10911
- return clamp01(frontmatter.confidence ?? 0.5);
10912
- }
10913
- }
10914
- function accessWeight(accessCount) {
10915
- const raw = accessCount ?? 0;
10916
- if (raw <= 0) return 0;
10917
- return clamp01(Math.log1p(raw) / Math.log1p(20));
10918
- }
10919
- function recencyWeight(frontmatter, nowMs) {
10920
- const lastTouch = frontmatter.lastAccessed ?? frontmatter.updated ?? frontmatter.created;
10921
- const ageDays = daysSince(lastTouch, nowMs);
10922
- return clamp01(1 - ageDays / 90);
10923
- }
10924
- function feedbackWeight(signals) {
10925
- const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);
10926
- return clamp01((raw + 1) / 2);
10927
- }
10928
- function boundedFeedbackScore(signals) {
10929
- const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);
10930
- if (!Number.isFinite(raw)) return 0;
10931
- if (raw < -1) return -1;
10932
- if (raw > 1) return 1;
10933
- return raw;
10934
- }
10935
- function isProtectedMemory(frontmatter, policy) {
10936
- return frontmatter.policyClass === "protected" || policy.protectedCategories.includes(frontmatter.category);
10937
- }
10938
- function resolveLifecycleState(frontmatter) {
10939
- if (frontmatter.status === "archived") return "archived";
10940
- return frontmatter.lifecycleState ?? "candidate";
10941
- }
10942
- function computeHeat(memory, now, signals) {
10943
- const frontmatter = memory.frontmatter;
10944
- if (frontmatter.status === "archived") return 0;
10945
- const inputs = computeLifecycleValueInputs(memory, now, signals);
10946
- const score = inputs.confidence * 0.25 + inputs.access * 0.3 + inputs.recency * 0.2 + inputs.importance * 0.15 + inputs.feedback * 0.1 - inputs.disputedPenalty;
10947
- return clamp01(score);
10948
- }
10949
- function computeLifecycleValueInputs(memory, now, signals) {
10950
- const frontmatter = memory.frontmatter;
10951
- const nowMs = now.getTime();
10952
- return {
10953
- confidence: confidenceTierWeight(frontmatter),
10954
- access: accessWeight(frontmatter.accessCount),
10955
- recency: recencyWeight(frontmatter, nowMs),
10956
- importance: clamp01(frontmatter.importance?.score ?? 0.5),
10957
- feedback: feedbackWeight(signals),
10958
- disputedPenalty: frontmatter.verificationState === "disputed" ? 0.2 : 0
10959
- };
10960
- }
10961
- function computeDecay(memory, now, signals) {
10962
- const frontmatter = memory.frontmatter;
10963
- if (frontmatter.status === "archived") return 1;
10964
- const nowMs = now.getTime();
10965
- const ageDays = daysSince(frontmatter.updated ?? frontmatter.created, nowMs);
10966
- const staleAccessDays = daysSince(frontmatter.lastAccessed, nowMs);
10967
- const ageRisk = clamp01(ageDays / 180);
10968
- const staleAccessRisk = clamp01(staleAccessDays / 120);
10969
- const confidenceRisk = 1 - confidenceTierWeight(frontmatter);
10970
- const feedbackRisk = clamp01((boundedFeedbackScore(signals) * -1 + 1) / 2);
10971
- const heat = computeHeat(memory, now, signals);
10972
- const score = ageRisk * 0.3 + staleAccessRisk * 0.25 + confidenceRisk * 0.2 + feedbackRisk * 0.1 + (1 - heat) * 0.15;
10973
- return clamp01(score);
10974
- }
10975
- function toTerminalDisputedState(currentState) {
10976
- if (currentState === "archived") return "archived";
10977
- return "stale";
10978
- }
10979
- function isActiveEligible(verificationState) {
10980
- return verificationState === "user_confirmed" || verificationState === "system_inferred";
10981
- }
10982
- function decideLifecycleTransition(memory, policy, now, signals) {
10983
- const mergedPolicy = { ...DEFAULT_POLICY, ...policy };
10984
- const frontmatter = memory.frontmatter;
10985
- const currentState = resolveLifecycleState(frontmatter);
10986
- const heatScore = computeHeat(memory, now, signals);
10987
- const decayScore = computeDecay(memory, now, signals);
10988
- const protectedMemory = isProtectedMemory(frontmatter, mergedPolicy);
10989
- if (currentState === "archived") {
10990
- return {
10991
- currentState,
10992
- nextState: "archived",
10993
- heatScore,
10994
- decayScore,
10995
- changed: false,
10996
- reason: "archived_is_terminal"
10997
- };
10998
- }
10999
- if (frontmatter.verificationState === "disputed") {
11000
- const nextState = toTerminalDisputedState(currentState);
11001
- return {
11002
- currentState,
11003
- nextState,
11004
- heatScore,
11005
- decayScore,
11006
- changed: nextState !== currentState,
11007
- reason: "disputed_memories_do_not_promote_to_active"
11008
- };
11009
- }
11010
- if (decayScore >= mergedPolicy.archiveDecayThreshold && !protectedMemory) {
11011
- return {
11012
- currentState,
11013
- nextState: "archived",
11014
- heatScore,
11015
- decayScore,
11016
- changed: true,
11017
- reason: "decay_exceeded_archive_threshold"
11018
- };
11019
- }
11020
- if (decayScore >= mergedPolicy.staleDecayThreshold) {
11021
- return {
11022
- currentState,
11023
- nextState: "stale",
11024
- heatScore,
11025
- decayScore,
11026
- changed: currentState !== "stale",
11027
- reason: "decay_exceeded_stale_threshold"
11028
- };
11005
+ } catch {
11006
+ shouldBuild = true;
11007
+ }
11008
+ }
11009
+ if (!shouldBuild) return;
11010
+ const summary = await summarize2(capTmtSummaryInputs(weekSummaries, "persona"), "persona");
11011
+ const now = (/* @__PURE__ */ new Date()).toISOString();
11012
+ const fm = {
11013
+ level: "persona",
11014
+ periodStart: earliestStart ?? now,
11015
+ periodEnd: latestEnd ?? now,
11016
+ memoryCount: totalCount,
11017
+ sourceIds: [],
11018
+ builtAt: now
11019
+ };
11020
+ await writeFile12(nodePath, serialiseTmtNode(fm, summary), "utf8");
11021
+ } catch (err) {
11022
+ console.warn(`[engram] tmt: persona node build failed (ignored): ${err}`);
11023
+ }
11029
11024
  }
11030
- if (heatScore >= mergedPolicy.promoteHeatThreshold) {
11031
- const nextState = isActiveEligible(frontmatter.verificationState) ? "active" : "validated";
11032
- return {
11033
- currentState,
11034
- nextState,
11035
- heatScore,
11036
- decayScore,
11037
- changed: currentState !== nextState,
11038
- reason: "heat_exceeded_promote_threshold"
11039
- };
11025
+ /**
11026
+ * Return the summary text of the most relevant TMT node for a given prompt.
11027
+ * Preference: day node for today > most recent day node.
11028
+ * Returns null if no nodes exist or feature is disabled.
11029
+ */
11030
+ async getMostRelevantNode() {
11031
+ if (!this.cfg.temporalMemoryTreeEnabled) return null;
11032
+ try {
11033
+ const dir = tmtDir(this.baseDir);
11034
+ if (!fs.existsSync(dir)) return null;
11035
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
11036
+ const todayDay = dayNodePath(this.baseDir, today);
11037
+ if (fs.existsSync(todayDay)) {
11038
+ const content = await readFile13(todayDay, "utf8");
11039
+ const summary = content.replace(/^---[\s\S]*?---\n\n?/, "").trim();
11040
+ if (summary) return { level: "day", summary };
11041
+ }
11042
+ let entries = [];
11043
+ try {
11044
+ entries = await readdir7(dir);
11045
+ } catch {
11046
+ return null;
11047
+ }
11048
+ const dateDirs = entries.filter((e) => /^\d{4}-\d{2}-\d{2}$/.test(e)).sort().reverse();
11049
+ for (const dateDir of dateDirs) {
11050
+ const dayPath = dayNodePath(this.baseDir, dateDir);
11051
+ if (fs.existsSync(dayPath)) {
11052
+ const content = await readFile13(dayPath, "utf8");
11053
+ const summary = content.replace(/^---[\s\S]*?---\n\n?/, "").trim();
11054
+ if (summary) return { level: "day", summary };
11055
+ }
11056
+ }
11057
+ return null;
11058
+ } catch {
11059
+ return null;
11060
+ }
11040
11061
  }
11041
- return {
11042
- currentState,
11043
- nextState: currentState,
11044
- heatScore,
11045
- decayScore,
11046
- changed: false,
11047
- reason: "no_transition"
11048
- };
11049
- }
11062
+ };
11050
11063
 
11051
11064
  // src/temporal-index.ts
11052
11065
  import * as fs2 from "fs";
@@ -13125,6 +13138,160 @@ var RoutingRulesStore = class {
13125
13138
  }
13126
13139
  };
13127
13140
 
13141
+ // src/policy-runtime.ts
13142
+ import path26 from "path";
13143
+ import { mkdir as mkdir19, readFile as readFile18, rename as rename3, writeFile as writeFile17 } from "fs/promises";
13144
+ var RUNTIME_POLICY_VERSION = 1;
13145
+ var RUNTIME_POLICY_FILE = "policy-runtime.json";
13146
+ var RUNTIME_POLICY_PREV_FILE = "policy-runtime.prev.json";
13147
+ function sanitizeRuntimePolicyValues(values, options) {
13148
+ const out = {};
13149
+ if (typeof values.recencyWeight === "number") {
13150
+ out.recencyWeight = clamp01(values.recencyWeight);
13151
+ }
13152
+ if (typeof values.lifecyclePromoteHeatThreshold === "number") {
13153
+ out.lifecyclePromoteHeatThreshold = clampLifecycleThreshold(values.lifecyclePromoteHeatThreshold);
13154
+ }
13155
+ if (typeof values.lifecycleStaleDecayThreshold === "number") {
13156
+ const staleDecayThreshold = clampLifecycleThreshold(values.lifecycleStaleDecayThreshold);
13157
+ const maxStaleDecayThreshold = typeof options?.maxStaleDecayThreshold === "number" ? clampLifecycleThreshold(options.maxStaleDecayThreshold) : 1;
13158
+ out.lifecycleStaleDecayThreshold = Math.min(staleDecayThreshold, maxStaleDecayThreshold);
13159
+ }
13160
+ if (typeof values.cronRecallInstructionHeavyTokenCap === "number") {
13161
+ out.cronRecallInstructionHeavyTokenCap = clampInstructionHeavyTokenCap(
13162
+ values.cronRecallInstructionHeavyTokenCap
13163
+ );
13164
+ }
13165
+ return out;
13166
+ }
13167
+ function isRuntimeParameter(parameter) {
13168
+ return parameter === "recencyWeight" || parameter === "lifecyclePromoteHeatThreshold" || parameter === "lifecycleStaleDecayThreshold" || parameter === "cronRecallInstructionHeavyTokenCap";
13169
+ }
13170
+ async function readRuntimePolicySnapshot(filePath, options) {
13171
+ try {
13172
+ const raw = await readFile18(filePath, "utf-8");
13173
+ const parsed = JSON.parse(raw);
13174
+ 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) {
13175
+ return null;
13176
+ }
13177
+ return {
13178
+ version: parsed.version,
13179
+ updatedAt: parsed.updatedAt,
13180
+ values: sanitizeRuntimePolicyValues(parsed.values, {
13181
+ maxStaleDecayThreshold: options?.maxStaleDecayThreshold
13182
+ }),
13183
+ sourceAdjustmentCount: parsed.sourceAdjustmentCount
13184
+ };
13185
+ } catch {
13186
+ return null;
13187
+ }
13188
+ }
13189
+ async function writeSnapshotAtomic(filePath, snapshot) {
13190
+ const tempPath = `${filePath}.tmp`;
13191
+ await mkdir19(path26.dirname(filePath), { recursive: true });
13192
+ await writeFile17(tempPath, `${JSON.stringify(snapshot, null, 2)}
13193
+ `, "utf-8");
13194
+ await rename3(tempPath, filePath);
13195
+ }
13196
+ var PolicyRuntimeManager = class {
13197
+ constructor(memoryDir, config) {
13198
+ this.memoryDir = memoryDir;
13199
+ this.config = config;
13200
+ const stateDir2 = path26.join(memoryDir, "state");
13201
+ this.runtimePath = path26.join(stateDir2, RUNTIME_POLICY_FILE);
13202
+ this.runtimePrevPath = path26.join(stateDir2, RUNTIME_POLICY_PREV_FILE);
13203
+ }
13204
+ runtimePath;
13205
+ runtimePrevPath;
13206
+ async loadRuntimeValues() {
13207
+ const snapshot = await readRuntimePolicySnapshot(this.runtimePath, {
13208
+ maxStaleDecayThreshold: this.config.lifecycleArchiveDecayThreshold
13209
+ });
13210
+ if (!snapshot) return null;
13211
+ return snapshot.values;
13212
+ }
13213
+ async rollback() {
13214
+ const previous = await readRuntimePolicySnapshot(this.runtimePrevPath, {
13215
+ maxStaleDecayThreshold: this.config.lifecycleArchiveDecayThreshold
13216
+ });
13217
+ if (!previous) return false;
13218
+ await writeSnapshotAtomic(this.runtimePath, {
13219
+ ...previous,
13220
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13221
+ });
13222
+ return true;
13223
+ }
13224
+ async applyFromBehaviorState(state) {
13225
+ const adjustmentCount = state.adjustments.length;
13226
+ if (adjustmentCount === 0) {
13227
+ return { applied: false, rolledBack: false, values: await this.loadRuntimeValues(), reason: "no_adjustments" };
13228
+ }
13229
+ const protectedSet = /* @__PURE__ */ new Set([
13230
+ ...this.config.behaviorLoopProtectedParams,
13231
+ ...state.protectedParams
13232
+ ]);
13233
+ const existing = await readRuntimePolicySnapshot(this.runtimePath, {
13234
+ maxStaleDecayThreshold: this.config.lifecycleArchiveDecayThreshold
13235
+ });
13236
+ const candidate = {
13237
+ recencyWeight: existing?.values.recencyWeight ?? this.config.recencyWeight,
13238
+ lifecyclePromoteHeatThreshold: existing?.values.lifecyclePromoteHeatThreshold ?? this.config.lifecyclePromoteHeatThreshold,
13239
+ lifecycleStaleDecayThreshold: existing?.values.lifecycleStaleDecayThreshold ?? this.config.lifecycleStaleDecayThreshold,
13240
+ cronRecallInstructionHeavyTokenCap: existing?.values.cronRecallInstructionHeavyTokenCap ?? this.config.cronRecallInstructionHeavyTokenCap
13241
+ };
13242
+ for (const adjustment of state.adjustments) {
13243
+ if (!isRuntimeParameter(adjustment.parameter)) {
13244
+ let rolledBack = false;
13245
+ if (existing) {
13246
+ await writeSnapshotAtomic(this.runtimePath, existing);
13247
+ rolledBack = true;
13248
+ } else {
13249
+ rolledBack = await this.rollback();
13250
+ }
13251
+ return {
13252
+ applied: false,
13253
+ rolledBack,
13254
+ values: await this.loadRuntimeValues(),
13255
+ reason: `invalid_parameter:${adjustment.parameter}`
13256
+ };
13257
+ }
13258
+ if (protectedSet.has(adjustment.parameter)) {
13259
+ continue;
13260
+ }
13261
+ if (!Number.isFinite(adjustment.nextValue)) {
13262
+ let rolledBack = false;
13263
+ if (existing) {
13264
+ await writeSnapshotAtomic(this.runtimePath, existing);
13265
+ rolledBack = true;
13266
+ } else {
13267
+ rolledBack = await this.rollback();
13268
+ }
13269
+ return {
13270
+ applied: false,
13271
+ rolledBack,
13272
+ values: await this.loadRuntimeValues(),
13273
+ reason: `invalid_value:${adjustment.parameter}`
13274
+ };
13275
+ }
13276
+ candidate[adjustment.parameter] = adjustment.nextValue;
13277
+ }
13278
+ const sanitized = sanitizeRuntimePolicyValues(candidate, {
13279
+ maxStaleDecayThreshold: this.config.lifecycleArchiveDecayThreshold
13280
+ });
13281
+ if (existing) {
13282
+ await writeSnapshotAtomic(this.runtimePrevPath, existing);
13283
+ }
13284
+ const nextSnapshot = {
13285
+ version: RUNTIME_POLICY_VERSION,
13286
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13287
+ values: sanitized,
13288
+ sourceAdjustmentCount: adjustmentCount
13289
+ };
13290
+ await writeSnapshotAtomic(this.runtimePath, nextSnapshot);
13291
+ return { applied: true, rolledBack: false, values: sanitized, reason: "applied" };
13292
+ }
13293
+ };
13294
+
13128
13295
  // src/behavior-signals.ts
13129
13296
  import { createHash as createHash5 } from "crypto";
13130
13297
  function normalizeSignalText(input) {
@@ -13316,11 +13483,11 @@ function mergeGraphExpandedResults(primary, expanded) {
13316
13483
  return Array.from(mergedByPath.values());
13317
13484
  }
13318
13485
  function graphPathRelativeToStorage(storageDir, candidatePath) {
13319
- const absolutePath = path26.isAbsolute(candidatePath) ? candidatePath : path26.resolve(storageDir, candidatePath);
13320
- const rel = path26.relative(storageDir, absolutePath);
13486
+ const absolutePath = path27.isAbsolute(candidatePath) ? candidatePath : path27.resolve(storageDir, candidatePath);
13487
+ const rel = path27.relative(storageDir, absolutePath);
13321
13488
  if (!rel || rel === ".") return null;
13322
13489
  if (rel.startsWith("..")) return null;
13323
- return rel.split(path26.sep).join("/");
13490
+ return rel.split(path27.sep).join("/");
13324
13491
  }
13325
13492
  function normalizeGraphActivationScore(score) {
13326
13493
  const bounded = Number.isFinite(score) && score > 0 ? score : 0;
@@ -13395,7 +13562,7 @@ function buildMemoryPathById(allMemsForGraph, storageDir) {
13395
13562
  for (const mem of allMemsForGraph ?? []) {
13396
13563
  const id = mem.frontmatter.id;
13397
13564
  if (!id) continue;
13398
- pathById.set(id, path26.relative(storageDir, mem.path));
13565
+ pathById.set(id, path27.relative(storageDir, mem.path));
13399
13566
  }
13400
13567
  return pathById;
13401
13568
  }
@@ -13403,7 +13570,7 @@ function appendMemoryToGraphContext(options) {
13403
13570
  if (!Array.isArray(options.allMemsForGraph)) return;
13404
13571
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
13405
13572
  options.allMemsForGraph.push({
13406
- path: path26.join(options.storageDir, options.memoryRelPath),
13573
+ path: path27.join(options.storageDir, options.memoryRelPath),
13407
13574
  content: options.content,
13408
13575
  frontmatter: {
13409
13576
  id: options.memoryId,
@@ -13423,15 +13590,15 @@ function resolvePersistedMemoryRelativePath(options) {
13423
13590
  const persisted = options.pathById.get(options.memoryId);
13424
13591
  if (persisted) return persisted;
13425
13592
  if (options.category === "correction") {
13426
- return path26.join("corrections", `${options.memoryId}.md`);
13593
+ return path27.join("corrections", `${options.memoryId}.md`);
13427
13594
  }
13428
13595
  const idParts = options.memoryId.split("-");
13429
13596
  const maybeTimestamp = Number(idParts[1]);
13430
13597
  if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
13431
13598
  const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
13432
- return path26.join("facts", day, `${options.memoryId}.md`);
13599
+ return path27.join("facts", day, `${options.memoryId}.md`);
13433
13600
  }
13434
- return path26.join("facts", `${options.memoryId}.md`);
13601
+ return path27.join("facts", `${options.memoryId}.md`);
13435
13602
  }
13436
13603
  var Orchestrator = class _Orchestrator {
13437
13604
  storage;
@@ -13490,6 +13657,8 @@ var Orchestrator = class _Orchestrator {
13490
13657
  lastRecallFailureLogAtMs = 0;
13491
13658
  lastRecallFailureAtMs = 0;
13492
13659
  suppressedRecallFailures = 0;
13660
+ policyRuntime;
13661
+ runtimePolicyValues = null;
13493
13662
  // Initialization gate: recall() awaits this before proceeding
13494
13663
  initPromise = null;
13495
13664
  resolveInit = null;
@@ -13539,7 +13708,7 @@ var Orchestrator = class _Orchestrator {
13539
13708
  this.compounding = config.compoundingEnabled ? new CompoundingEngine(config) : void 0;
13540
13709
  this.buffer = new SmartBuffer(config, this.storage);
13541
13710
  this.transcript = new TranscriptManager(config);
13542
- this.conversationIndexDir = path26.join(config.memoryDir, "conversation-index", "chunks");
13711
+ this.conversationIndexDir = path27.join(config.memoryDir, "conversation-index", "chunks");
13543
13712
  this.modelRegistry = new ModelRegistry(config.memoryDir);
13544
13713
  this.relevance = new RelevanceStore(config.memoryDir);
13545
13714
  this.negatives = new NegativeExampleStore(config.memoryDir);
@@ -13551,11 +13720,12 @@ var Orchestrator = class _Orchestrator {
13551
13720
  bands: config.sessionObserverBands ?? []
13552
13721
  });
13553
13722
  this.embeddingFallback = new EmbeddingFallback(config);
13723
+ this.policyRuntime = new PolicyRuntimeManager(config.memoryDir, config);
13554
13724
  this.summarizer = new HourlySummarizer(config, config.gatewayConfig, this.modelRegistry, this.transcript);
13555
13725
  this.localLlm = new LocalLlmClient(config, this.modelRegistry);
13556
13726
  this.extraction = new ExtractionEngine(config, this.localLlm, config.gatewayConfig, this.modelRegistry);
13557
13727
  this.threading = new ThreadingManager(
13558
- path26.join(config.memoryDir, "threads"),
13728
+ path27.join(config.memoryDir, "threads"),
13559
13729
  config.threadingGapMinutes
13560
13730
  );
13561
13731
  this.tmtBuilder = new TmtBuilder(config.memoryDir, {
@@ -13583,6 +13753,37 @@ var Orchestrator = class _Orchestrator {
13583
13753
  }
13584
13754
  return this.boxBuilders.get(dir);
13585
13755
  }
13756
+ effectiveRecencyWeight() {
13757
+ return applyRuntimeRetrievalPolicy(
13758
+ { recencyWeight: this.config.recencyWeight },
13759
+ this.runtimePolicyValues
13760
+ ).recencyWeight;
13761
+ }
13762
+ effectiveCronRecallInstructionHeavyTokenCap() {
13763
+ return this.runtimePolicyValues?.cronRecallInstructionHeavyTokenCap ?? this.config.cronRecallInstructionHeavyTokenCap;
13764
+ }
13765
+ currentPolicyVersion() {
13766
+ const thresholds = this.effectiveLifecycleThresholds();
13767
+ const payload = {
13768
+ recencyWeight: this.effectiveRecencyWeight(),
13769
+ lifecyclePromoteHeatThreshold: thresholds.promoteHeatThreshold,
13770
+ lifecycleStaleDecayThreshold: thresholds.staleDecayThreshold,
13771
+ cronRecallInstructionHeavyTokenCap: this.effectiveCronRecallInstructionHeavyTokenCap()
13772
+ };
13773
+ return createHash6("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 12);
13774
+ }
13775
+ effectiveLifecycleThresholds() {
13776
+ const archiveDecayThreshold = this.config.lifecycleArchiveDecayThreshold;
13777
+ const staleDecayThreshold = Math.min(
13778
+ this.runtimePolicyValues?.lifecycleStaleDecayThreshold ?? this.config.lifecycleStaleDecayThreshold,
13779
+ archiveDecayThreshold
13780
+ );
13781
+ return {
13782
+ promoteHeatThreshold: this.runtimePolicyValues?.lifecyclePromoteHeatThreshold ?? this.config.lifecyclePromoteHeatThreshold,
13783
+ staleDecayThreshold,
13784
+ archiveDecayThreshold
13785
+ };
13786
+ }
13586
13787
  routeEngineOptions() {
13587
13788
  const allowedNamespaces = this.config.namespacesEnabled ? Array.from(
13588
13789
  /* @__PURE__ */ new Set([
@@ -13691,8 +13892,9 @@ var Orchestrator = class _Orchestrator {
13691
13892
  await this.lastRecall.load();
13692
13893
  await this.tierMigrationStatus.load();
13693
13894
  await this.sessionObserver.load();
13895
+ this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
13694
13896
  if (this.config.factDeduplicationEnabled) {
13695
- const stateDir2 = path26.join(this.config.memoryDir, "state");
13897
+ const stateDir2 = path27.join(this.config.memoryDir, "state");
13696
13898
  this.contentHashIndex = new ContentHashIndex(stateDir2);
13697
13899
  await this.contentHashIndex.load();
13698
13900
  log.info(`content-hash dedup: loaded ${this.contentHashIndex.size} hashes`);
@@ -13730,7 +13932,7 @@ var Orchestrator = class _Orchestrator {
13730
13932
  if (available) {
13731
13933
  log.info(`Conversation index QMD: available ${this.conversationQmd.debugStatus()}`);
13732
13934
  const collectionState = await this.conversationQmd.ensureCollection(
13733
- path26.join(this.config.memoryDir, "conversation-index")
13935
+ path27.join(this.config.memoryDir, "conversation-index")
13734
13936
  );
13735
13937
  if (collectionState === "missing") {
13736
13938
  this.config.conversationIndexEnabled = false;
@@ -13766,6 +13968,16 @@ var Orchestrator = class _Orchestrator {
13766
13968
  this.resolveInit = null;
13767
13969
  }
13768
13970
  }
13971
+ async applyBehaviorRuntimePolicy(state) {
13972
+ const result = await this.policyRuntime.applyFromBehaviorState(state);
13973
+ this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
13974
+ return result;
13975
+ }
13976
+ async rollbackBehaviorRuntimePolicy() {
13977
+ const rolledBack = await this.policyRuntime.rollback();
13978
+ this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
13979
+ return rolledBack;
13980
+ }
13769
13981
  async maybeRunFileHygiene() {
13770
13982
  const hygiene = this.config.fileHygiene;
13771
13983
  if (!hygiene?.enabled) return;
@@ -13774,12 +13986,12 @@ var Orchestrator = class _Orchestrator {
13774
13986
  this.lastFileHygieneRunAtMs = now;
13775
13987
  if (hygiene.rotateEnabled) {
13776
13988
  for (const rel of hygiene.rotatePaths) {
13777
- const abs = path26.isAbsolute(rel) ? rel : path26.join(this.config.workspaceDir, rel);
13989
+ const abs = path27.isAbsolute(rel) ? rel : path27.join(this.config.workspaceDir, rel);
13778
13990
  try {
13779
- const raw = await readFile18(abs, "utf-8");
13991
+ const raw = await readFile19(abs, "utf-8");
13780
13992
  if (raw.length > hygiene.rotateMaxBytes) {
13781
- const archiveDir = path26.join(this.config.workspaceDir, hygiene.archiveDir);
13782
- const base = path26.basename(abs);
13993
+ const archiveDir = path27.join(this.config.workspaceDir, hygiene.archiveDir);
13994
+ const base = path27.basename(abs);
13783
13995
  const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
13784
13996
  const { newContent } = await rotateMarkdownFileToArchive({
13785
13997
  filePath: abs,
@@ -13787,7 +13999,7 @@ var Orchestrator = class _Orchestrator {
13787
13999
  archivePrefix: prefix,
13788
14000
  keepTailChars: hygiene.rotateKeepTailChars
13789
14001
  });
13790
- await writeFile17(abs, newContent, "utf-8");
14002
+ await writeFile18(abs, newContent, "utf-8");
13791
14003
  }
13792
14004
  } catch {
13793
14005
  }
@@ -13804,8 +14016,8 @@ var Orchestrator = class _Orchestrator {
13804
14016
  log.warn(w.message);
13805
14017
  }
13806
14018
  if (hygiene.warningsLogEnabled && warnings.length > 0) {
13807
- const fp = path26.join(this.config.memoryDir, hygiene.warningsLogPath);
13808
- await mkdir19(path26.dirname(fp), { recursive: true });
14019
+ const fp = path27.join(this.config.memoryDir, hygiene.warningsLogPath);
14020
+ await mkdir20(path27.dirname(fp), { recursive: true });
13809
14021
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
13810
14022
  const block = `
13811
14023
 
@@ -13814,11 +14026,11 @@ var Orchestrator = class _Orchestrator {
13814
14026
  ` + warnings.map((w) => `- ${w.message}`).join("\n") + "\n";
13815
14027
  let existing = "";
13816
14028
  try {
13817
- existing = await readFile18(fp, "utf-8");
14029
+ existing = await readFile19(fp, "utf-8");
13818
14030
  } catch {
13819
14031
  existing = "# Engram File Hygiene Warnings\n";
13820
14032
  }
13821
- await writeFile17(fp, existing + block, "utf-8");
14033
+ await writeFile18(fp, existing + block, "utf-8");
13822
14034
  }
13823
14035
  }
13824
14036
  }
@@ -13894,9 +14106,9 @@ var Orchestrator = class _Orchestrator {
13894
14106
  }
13895
14107
  async getLastGraphRecallSnapshot(namespace) {
13896
14108
  const storage = await this.getStorage(namespace);
13897
- const snapshotPath = path26.join(storage.dir, "state", "last_graph_recall.json");
14109
+ const snapshotPath = path27.join(storage.dir, "state", "last_graph_recall.json");
13898
14110
  try {
13899
- const raw = await readFile18(snapshotPath, "utf-8");
14111
+ const raw = await readFile19(snapshotPath, "utf-8");
13900
14112
  const parsed = JSON.parse(raw);
13901
14113
  if (!parsed || typeof parsed !== "object") return null;
13902
14114
  return {
@@ -13965,7 +14177,7 @@ ${r.snippet.trim()}
13965
14177
  const entries = await readdir11(dir, { withFileTypes: true });
13966
14178
  let total = 0;
13967
14179
  for (const entry of entries) {
13968
- const fullPath = path26.join(dir, entry.name);
14180
+ const fullPath = path27.join(dir, entry.name);
13969
14181
  if (entry.isDirectory()) {
13970
14182
  total += await this.countConversationChunkDocs(fullPath);
13971
14183
  continue;
@@ -14281,7 +14493,7 @@ ${r.snippet.trim()}
14281
14493
  const seedRelativePaths = seedCandidates.map((result) => graphPathRelativeToStorage(storage.dir, result.path)).filter((value) => typeof value === "string" && value.length > 0);
14282
14494
  if (seedRelativePaths.length === 0) continue;
14283
14495
  const seedRecallScore = seedCandidates.reduce((max, item) => Math.max(max, item.score), 0);
14284
- seedPaths.push(...seedRelativePaths.map((rel) => path26.join(storage.dir, rel)));
14496
+ seedPaths.push(...seedRelativePaths.map((rel) => path27.join(storage.dir, rel)));
14285
14497
  const seedSet = new Set(seedRelativePaths);
14286
14498
  const expanded = await this.graphIndexFor(storage).spreadingActivation(
14287
14499
  seedRelativePaths,
@@ -14290,7 +14502,7 @@ ${r.snippet.trim()}
14290
14502
  if (expanded.length === 0) continue;
14291
14503
  for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
14292
14504
  if (seedSet.has(candidate.path)) continue;
14293
- const memoryPath = path26.resolve(storage.dir, candidate.path);
14505
+ const memoryPath = path27.resolve(storage.dir, candidate.path);
14294
14506
  const memory = await storage.readMemoryByPath(memoryPath);
14295
14507
  if (!memory) continue;
14296
14508
  if (isArtifactMemoryPath(memory.path)) continue;
@@ -14313,7 +14525,7 @@ ${r.snippet.trim()}
14313
14525
  path: memory.path,
14314
14526
  score,
14315
14527
  namespace,
14316
- seed: path26.resolve(storage.dir, candidate.seed),
14528
+ seed: path27.resolve(storage.dir, candidate.seed),
14317
14529
  hopDepth: candidate.hopDepth,
14318
14530
  decayedWeight: candidate.decayedWeight,
14319
14531
  graphType: candidate.graphType
@@ -14328,8 +14540,8 @@ ${r.snippet.trim()}
14328
14540
  }
14329
14541
  async recordLastGraphRecallSnapshot(options) {
14330
14542
  try {
14331
- const snapshotPath = path26.join(options.storage.dir, "state", "last_graph_recall.json");
14332
- await mkdir19(path26.dirname(snapshotPath), { recursive: true });
14543
+ const snapshotPath = path27.join(options.storage.dir, "state", "last_graph_recall.json");
14544
+ await mkdir20(path27.dirname(snapshotPath), { recursive: true });
14333
14545
  const now = (/* @__PURE__ */ new Date()).toISOString();
14334
14546
  const totalSeedCount = options.seedPaths.length;
14335
14547
  const totalExpandedCount = options.expandedPaths.length;
@@ -14346,7 +14558,7 @@ ${r.snippet.trim()}
14346
14558
  seeds,
14347
14559
  expanded
14348
14560
  };
14349
- await writeFile17(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
14561
+ await writeFile18(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
14350
14562
  } catch (err) {
14351
14563
  log.debug(`last graph recall write failed: ${err}`);
14352
14564
  }
@@ -14359,11 +14571,12 @@ ${r.snippet.trim()}
14359
14571
  const queryPolicy = buildRecallQueryPolicy(prompt, sessionKey, {
14360
14572
  cronRecallPolicyEnabled: this.config.cronRecallPolicyEnabled,
14361
14573
  cronRecallNormalizedQueryMaxChars: this.config.cronRecallNormalizedQueryMaxChars,
14362
- cronRecallInstructionHeavyTokenCap: this.config.cronRecallInstructionHeavyTokenCap,
14574
+ cronRecallInstructionHeavyTokenCap: this.effectiveCronRecallInstructionHeavyTokenCap(),
14363
14575
  cronConversationRecallMode: this.config.cronConversationRecallMode
14364
14576
  });
14365
14577
  const retrievalQuery = queryPolicy.retrievalQuery || prompt;
14366
14578
  const retrievalQueryHash = createHash6("sha256").update(retrievalQuery).digest("hex");
14579
+ const policyVersion = this.currentPolicyVersion();
14367
14580
  let impressionRecorded = false;
14368
14581
  let recallSource = "none";
14369
14582
  let recalledMemoryCount = 0;
@@ -14414,6 +14627,7 @@ ${r.snippet.trim()}
14414
14627
  recalledMemoryCount,
14415
14628
  injected: false,
14416
14629
  contextChars: 0,
14630
+ policyVersion,
14417
14631
  identityInjectionMode: identityInjectionModeUsed,
14418
14632
  identityInjectedChars,
14419
14633
  identityInjectionTruncated,
@@ -14985,6 +15199,7 @@ _Context: ${topQuestion.context}_`);
14985
15199
  sessionKey,
14986
15200
  query: retrievalQuery,
14987
15201
  memoryIds: [],
15202
+ policyVersion,
14988
15203
  identityInjection: {
14989
15204
  mode: identityInjectionModeUsed,
14990
15205
  injectedChars: identityInjectedChars,
@@ -15011,6 +15226,7 @@ _Context: ${topQuestion.context}_`);
15011
15226
  recalledMemoryCount,
15012
15227
  injected: context.length > 0,
15013
15228
  contextChars: context.length,
15229
+ policyVersion,
15014
15230
  identityInjectionMode: identityInjectionModeUsed,
15015
15231
  identityInjectedChars,
15016
15232
  identityInjectionTruncated,
@@ -15353,7 +15569,7 @@ _Context: ${topQuestion.context}_`);
15353
15569
  };
15354
15570
  this.tierMigrationInFlight = true;
15355
15571
  try {
15356
- const coldStorage = new StorageManager(path26.join(storage.dir, "cold"));
15572
+ const coldStorage = new StorageManager(path27.join(storage.dir, "cold"));
15357
15573
  const [hotMemories, coldMemories] = await Promise.all([
15358
15574
  storage.readAllMemories(),
15359
15575
  coldStorage.readAllMemories()
@@ -15947,7 +16163,7 @@ _Context: ${topQuestion.context}_`);
15947
16163
  const allMems = allMemsForGraph ?? [];
15948
16164
  for (const m of allMems) {
15949
16165
  if (m.frontmatter.entityRef === entityRef) {
15950
- const rel = path26.relative(storage.dir, m.path);
16166
+ const rel = path27.relative(storage.dir, m.path);
15951
16167
  if (rel !== memoryRelPath) entitySiblings.push(rel);
15952
16168
  }
15953
16169
  }
@@ -16449,10 +16665,11 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
16449
16665
  let updatedCount = 0;
16450
16666
  let disputedCount = 0;
16451
16667
  let evaluatedCount = 0;
16668
+ const thresholds = this.effectiveLifecycleThresholds();
16452
16669
  const policy = {
16453
- promoteHeatThreshold: this.config.lifecyclePromoteHeatThreshold,
16454
- staleDecayThreshold: this.config.lifecycleStaleDecayThreshold,
16455
- archiveDecayThreshold: this.config.lifecycleArchiveDecayThreshold,
16670
+ promoteHeatThreshold: thresholds.promoteHeatThreshold,
16671
+ staleDecayThreshold: thresholds.staleDecayThreshold,
16672
+ archiveDecayThreshold: thresholds.archiveDecayThreshold,
16456
16673
  protectedCategories: this.config.lifecycleProtectedCategories
16457
16674
  };
16458
16675
  const actionPriors = await this.buildLifecycleActionPriors();
@@ -16498,15 +16715,15 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
16498
16715
  staleRatio: total > 0 ? countsByState.stale / total : 0,
16499
16716
  disputedRatio: total > 0 ? disputedCount / total : 0,
16500
16717
  policy: {
16501
- promoteHeatThreshold: this.config.lifecyclePromoteHeatThreshold,
16502
- staleDecayThreshold: this.config.lifecycleStaleDecayThreshold,
16503
- archiveDecayThreshold: this.config.lifecycleArchiveDecayThreshold,
16718
+ promoteHeatThreshold: thresholds.promoteHeatThreshold,
16719
+ staleDecayThreshold: thresholds.staleDecayThreshold,
16720
+ archiveDecayThreshold: thresholds.archiveDecayThreshold,
16504
16721
  protectedCategories: this.config.lifecycleProtectedCategories
16505
16722
  }
16506
16723
  };
16507
- const metricsPath = path26.join(this.storage.dir, "state", "lifecycle-metrics.json");
16508
- await mkdir19(path26.dirname(metricsPath), { recursive: true });
16509
- await writeFile17(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
16724
+ const metricsPath = path27.join(this.storage.dir, "state", "lifecycle-metrics.json");
16725
+ await mkdir20(path27.dirname(metricsPath), { recursive: true });
16726
+ await writeFile18(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
16510
16727
  }
16511
16728
  /**
16512
16729
  * Archive old, low-importance, rarely-accessed facts (v6.0).
@@ -16760,6 +16977,7 @@ ${lines.join("\n\n")}`;
16760
16977
  sessionKey: options.sessionKey,
16761
16978
  query: options.retrievalQuery,
16762
16979
  memoryIds: unique,
16980
+ policyVersion: this.currentPolicyVersion(),
16763
16981
  identityInjection: options.identityInjection
16764
16982
  }).catch((err) => log.debug(`last recall record failed: ${err}`));
16765
16983
  }
@@ -16772,7 +16990,7 @@ ${lines.join("\n\n")}`;
16772
16990
  if (hits.length === 0) return [];
16773
16991
  const results = [];
16774
16992
  for (const hit of hits) {
16775
- const fullPath = path26.isAbsolute(hit.path) ? hit.path : path26.join(this.config.memoryDir, hit.path);
16993
+ const fullPath = path27.isAbsolute(hit.path) ? hit.path : path27.join(this.config.memoryDir, hit.path);
16776
16994
  const memory = await this.storage.readMemoryByPath(fullPath);
16777
16995
  if (!memory) continue;
16778
16996
  results.push({
@@ -17031,6 +17249,7 @@ ${lines.join("\n\n")}`;
17031
17249
  }
17032
17250
  let lifecycleFilteredCount = 0;
17033
17251
  const boosted = [];
17252
+ const recencyWeight2 = this.effectiveRecencyWeight();
17034
17253
  for (const r of results) {
17035
17254
  const memory = memoryByPath.get(r.path);
17036
17255
  let score = r.score;
@@ -17042,13 +17261,13 @@ ${lines.join("\n\n")}`;
17042
17261
  lifecycleFilteredCount += 1;
17043
17262
  continue;
17044
17263
  }
17045
- if (this.config.recencyWeight > 0) {
17264
+ if (recencyWeight2 > 0) {
17046
17265
  const createdAt = new Date(memory.frontmatter.created).getTime();
17047
17266
  const ageMs = now - createdAt;
17048
17267
  const ageDays = ageMs / (1e3 * 60 * 60 * 24);
17049
17268
  const halfLifeDays = 7;
17050
17269
  const recencyScore = Math.pow(0.5, ageDays / halfLifeDays);
17051
- score = score * (1 - this.config.recencyWeight) + recencyScore * this.config.recencyWeight;
17270
+ score = score * (1 - recencyWeight2) + recencyScore * recencyWeight2;
17052
17271
  }
17053
17272
  if (this.config.boostAccessCount && memory.frontmatter.accessCount) {
17054
17273
  const accessBoost = Math.log10(memory.frontmatter.accessCount + 1) / 3;
@@ -17229,8 +17448,8 @@ ${lines.join("\n\n")}`;
17229
17448
  }
17230
17449
  namespaceFromStorageDir(storageDir) {
17231
17450
  if (!this.config.namespacesEnabled) return this.config.defaultNamespace;
17232
- const resolvedStorageDir = path26.resolve(storageDir);
17233
- const resolvedMemoryDir = path26.resolve(this.config.memoryDir);
17451
+ const resolvedStorageDir = path27.resolve(storageDir);
17452
+ const resolvedMemoryDir = path27.resolve(this.config.memoryDir);
17234
17453
  if (resolvedStorageDir === resolvedMemoryDir) return this.config.defaultNamespace;
17235
17454
  const m = resolvedStorageDir.match(/[\\/]namespaces[\\/]([^\\/]+)$/);
17236
17455
  return m && m[1] ? m[1] : this.config.defaultNamespace;
@@ -17258,14 +17477,14 @@ ${lines.join("\n\n")}`;
17258
17477
  };
17259
17478
 
17260
17479
  // src/tools.ts
17261
- import path28 from "path";
17480
+ import path29 from "path";
17262
17481
  import { createHash as createHash7 } from "crypto";
17263
17482
  import { Type } from "@sinclair/typebox";
17264
17483
 
17265
17484
  // src/work/storage.ts
17266
- import path27 from "path";
17485
+ import path28 from "path";
17267
17486
  import { randomUUID } from "crypto";
17268
- import { mkdir as mkdir20, readdir as readdir12, readFile as readFile19, rm as rm3, writeFile as writeFile18 } from "fs/promises";
17487
+ import { mkdir as mkdir21, readdir as readdir12, readFile as readFile20, rm as rm3, writeFile as writeFile19 } from "fs/promises";
17269
17488
  var TASK_TRANSITIONS = {
17270
17489
  todo: /* @__PURE__ */ new Set(["in_progress", "blocked", "cancelled"]),
17271
17490
  in_progress: /* @__PURE__ */ new Set(["todo", "blocked", "done", "cancelled"]),
@@ -17340,22 +17559,22 @@ function ensureProjectStatus(value) {
17340
17559
  var WorkStorage = class {
17341
17560
  constructor(memoryDir) {
17342
17561
  this.memoryDir = memoryDir;
17343
- this.tasksDir = path27.join(memoryDir, "work", "tasks");
17344
- this.projectsDir = path27.join(memoryDir, "work", "projects");
17562
+ this.tasksDir = path28.join(memoryDir, "work", "tasks");
17563
+ this.projectsDir = path28.join(memoryDir, "work", "projects");
17345
17564
  }
17346
17565
  tasksDir;
17347
17566
  projectsDir;
17348
17567
  async ensureDirectories() {
17349
- await mkdir20(this.tasksDir, { recursive: true });
17350
- await mkdir20(this.projectsDir, { recursive: true });
17568
+ await mkdir21(this.tasksDir, { recursive: true });
17569
+ await mkdir21(this.projectsDir, { recursive: true });
17351
17570
  }
17352
17571
  taskPath(id) {
17353
17572
  assertValidWorkId(id, "task");
17354
- return path27.join(this.tasksDir, `${id}.md`);
17573
+ return path28.join(this.tasksDir, `${id}.md`);
17355
17574
  }
17356
17575
  projectPath(id) {
17357
17576
  assertValidWorkId(id, "project");
17358
- return path27.join(this.projectsDir, `${id}.md`);
17577
+ return path28.join(this.projectsDir, `${id}.md`);
17359
17578
  }
17360
17579
  serializeTask(task) {
17361
17580
  return `${serializeFrontmatter2(task)}
@@ -17427,7 +17646,7 @@ ${project.description}
17427
17646
  throw new Error(`project not found: ${task.projectId}`);
17428
17647
  }
17429
17648
  }
17430
- await writeFile18(this.taskPath(task.id), this.serializeTask(task), "utf-8");
17649
+ await writeFile19(this.taskPath(task.id), this.serializeTask(task), "utf-8");
17431
17650
  if (task.projectId) {
17432
17651
  await this.addTaskIdToProject(task.projectId, task.id, now);
17433
17652
  }
@@ -17435,7 +17654,7 @@ ${project.description}
17435
17654
  }
17436
17655
  async getTask(id) {
17437
17656
  try {
17438
- const raw = await readFile19(this.taskPath(id), "utf-8");
17657
+ const raw = await readFile20(this.taskPath(id), "utf-8");
17439
17658
  return this.parseTask(raw);
17440
17659
  } catch {
17441
17660
  return null;
@@ -17447,7 +17666,7 @@ ${project.description}
17447
17666
  const out = [];
17448
17667
  for (const entry of entries) {
17449
17668
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
17450
- const raw = await readFile19(path27.join(this.tasksDir, entry.name), "utf-8");
17669
+ const raw = await readFile20(path28.join(this.tasksDir, entry.name), "utf-8");
17451
17670
  const task = this.parseTask(raw);
17452
17671
  if (!task) continue;
17453
17672
  if (filter?.status && task.status !== filter.status) continue;
@@ -17489,7 +17708,7 @@ ${project.description}
17489
17708
  tags: patch.tags ?? existing.tags,
17490
17709
  updatedAt: now.toISOString()
17491
17710
  };
17492
- await writeFile18(this.taskPath(id), this.serializeTask(next), "utf-8");
17711
+ await writeFile19(this.taskPath(id), this.serializeTask(next), "utf-8");
17493
17712
  return next;
17494
17713
  }
17495
17714
  async transitionTask(id, nextStatus, now = /* @__PURE__ */ new Date()) {
@@ -17529,12 +17748,12 @@ ${project.description}
17529
17748
  createdAt: timestamp,
17530
17749
  updatedAt: timestamp
17531
17750
  };
17532
- await writeFile18(this.projectPath(project.id), this.serializeProject(project), "utf-8");
17751
+ await writeFile19(this.projectPath(project.id), this.serializeProject(project), "utf-8");
17533
17752
  return project;
17534
17753
  }
17535
17754
  async getProject(id) {
17536
17755
  try {
17537
- const raw = await readFile19(this.projectPath(id), "utf-8");
17756
+ const raw = await readFile20(this.projectPath(id), "utf-8");
17538
17757
  return this.parseProject(raw);
17539
17758
  } catch {
17540
17759
  return null;
@@ -17546,7 +17765,7 @@ ${project.description}
17546
17765
  const out = [];
17547
17766
  for (const entry of entries) {
17548
17767
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
17549
- const raw = await readFile19(path27.join(this.projectsDir, entry.name), "utf-8");
17768
+ const raw = await readFile20(path28.join(this.projectsDir, entry.name), "utf-8");
17550
17769
  const project = this.parseProject(raw);
17551
17770
  if (project) out.push(project);
17552
17771
  }
@@ -17563,7 +17782,7 @@ ${project.description}
17563
17782
  taskIds: patch.taskIds ? [...patch.taskIds].sort() : existing.taskIds,
17564
17783
  updatedAt: now.toISOString()
17565
17784
  };
17566
- await writeFile18(this.projectPath(id), this.serializeProject(next), "utf-8");
17785
+ await writeFile19(this.projectPath(id), this.serializeProject(next), "utf-8");
17567
17786
  return next;
17568
17787
  }
17569
17788
  async deleteProject(id) {
@@ -19102,7 +19321,7 @@ Best for:
19102
19321
  - Reviewing identity development over time`,
19103
19322
  parameters: Type.Object({}),
19104
19323
  async execute() {
19105
- const workspaceDir = path28.join(process.env.HOME ?? "~", ".openclaw", "workspace");
19324
+ const workspaceDir = path29.join(process.env.HOME ?? "~", ".openclaw", "workspace");
19106
19325
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
19107
19326
  if (!identity) {
19108
19327
  return toolResult("No identity file found. Identity reflections build automatically through conversations when identityEnabled is true.");
@@ -19611,12 +19830,13 @@ mistakes: ${res.mistakesCount} patterns`
19611
19830
  }
19612
19831
 
19613
19832
  // src/cli.ts
19614
- import path44 from "path";
19615
- import { access as access3, readFile as readFile30, readdir as readdir19, unlink as unlink5 } from "fs/promises";
19833
+ import path45 from "path";
19834
+ import { access as access3, readFile as readFile31, readdir as readdir19, unlink as unlink5 } from "fs/promises";
19835
+ import { createHash as createHash9 } from "crypto";
19616
19836
 
19617
19837
  // src/transfer/export-json.ts
19618
- import path30 from "path";
19619
- import { mkdir as mkdir22, readFile as readFile21 } from "fs/promises";
19838
+ import path31 from "path";
19839
+ import { mkdir as mkdir23, readFile as readFile22 } from "fs/promises";
19620
19840
 
19621
19841
  // src/transfer/constants.ts
19622
19842
  var EXPORT_FORMAT = "openclaw-engram-export";
@@ -19624,10 +19844,10 @@ var EXPORT_SCHEMA_VERSION = 1;
19624
19844
 
19625
19845
  // src/transfer/fs-utils.ts
19626
19846
  import { createHash as createHash8 } from "crypto";
19627
- import { mkdir as mkdir21, readdir as readdir13, readFile as readFile20, stat as stat6, writeFile as writeFile19 } from "fs/promises";
19628
- import path29 from "path";
19847
+ import { mkdir as mkdir22, readdir as readdir13, readFile as readFile21, stat as stat6, writeFile as writeFile20 } from "fs/promises";
19848
+ import path30 from "path";
19629
19849
  async function sha256File(filePath) {
19630
- const buf = await readFile20(filePath);
19850
+ const buf = await readFile21(filePath);
19631
19851
  const sha256 = createHash8("sha256").update(buf).digest("hex");
19632
19852
  return { sha256, bytes: buf.byteLength };
19633
19853
  }
@@ -19637,11 +19857,11 @@ function sha256String(content) {
19637
19857
  return { sha256, bytes: buf.byteLength };
19638
19858
  }
19639
19859
  async function writeJsonFile(filePath, value) {
19640
- await mkdir21(path29.dirname(filePath), { recursive: true });
19641
- await writeFile19(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
19860
+ await mkdir22(path30.dirname(filePath), { recursive: true });
19861
+ await writeFile20(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
19642
19862
  }
19643
19863
  async function readJsonFile(filePath) {
19644
- const raw = await readFile20(filePath, "utf-8");
19864
+ const raw = await readFile21(filePath, "utf-8");
19645
19865
  return JSON.parse(raw);
19646
19866
  }
19647
19867
  async function listFilesRecursive(rootDir) {
@@ -19649,7 +19869,7 @@ async function listFilesRecursive(rootDir) {
19649
19869
  async function walk(dir) {
19650
19870
  const entries = await readdir13(dir, { withFileTypes: true });
19651
19871
  for (const ent of entries) {
19652
- const fp = path29.join(dir, ent.name);
19872
+ const fp = path30.join(dir, ent.name);
19653
19873
  if (ent.isDirectory()) {
19654
19874
  await walk(fp);
19655
19875
  } else if (ent.isFile()) {
@@ -19669,11 +19889,11 @@ async function fileExists(filePath) {
19669
19889
  }
19670
19890
  }
19671
19891
  function toPosixRelPath(absPath, rootDir) {
19672
- const rel = path29.relative(rootDir, absPath);
19673
- return rel.split(path29.sep).join("/");
19892
+ const rel = path30.relative(rootDir, absPath);
19893
+ return rel.split(path30.sep).join("/");
19674
19894
  }
19675
19895
  function fromPosixRelPath(relPath) {
19676
- return relPath.split("/").join(path29.sep);
19896
+ return relPath.split("/").join(path30.sep);
19677
19897
  }
19678
19898
 
19679
19899
  // src/transfer/export-json.ts
@@ -19689,24 +19909,24 @@ function shouldExclude(relPosix, includeTranscripts) {
19689
19909
  }
19690
19910
  async function exportJsonBundle(opts) {
19691
19911
  const includeTranscripts = opts.includeTranscripts === true;
19692
- const outDirAbs = path30.resolve(opts.outDir);
19693
- await mkdir22(outDirAbs, { recursive: true });
19694
- const memoryDirAbs = path30.resolve(opts.memoryDir);
19912
+ const outDirAbs = path31.resolve(opts.outDir);
19913
+ await mkdir23(outDirAbs, { recursive: true });
19914
+ const memoryDirAbs = path31.resolve(opts.memoryDir);
19695
19915
  const filesAbs = await listFilesRecursive(memoryDirAbs);
19696
19916
  const records = [];
19697
19917
  const manifestFiles = [];
19698
19918
  for (const abs of filesAbs) {
19699
19919
  const relPosix = toPosixRelPath(abs, memoryDirAbs);
19700
19920
  if (shouldExclude(relPosix, includeTranscripts)) continue;
19701
- const content = await readFile21(abs, "utf-8");
19921
+ const content = await readFile22(abs, "utf-8");
19702
19922
  records.push({ path: relPosix, content });
19703
19923
  const { sha256, bytes } = await sha256File(abs);
19704
19924
  manifestFiles.push({ path: relPosix, sha256, bytes });
19705
19925
  }
19706
19926
  if (opts.includeWorkspaceIdentity !== false && opts.workspaceDir) {
19707
- const identityPath = path30.join(opts.workspaceDir, "IDENTITY.md");
19927
+ const identityPath = path31.join(opts.workspaceDir, "IDENTITY.md");
19708
19928
  try {
19709
- const content = await readFile21(identityPath, "utf-8");
19929
+ const content = await readFile22(identityPath, "utf-8");
19710
19930
  const relPath = "workspace/IDENTITY.md";
19711
19931
  records.push({ path: relPath, content });
19712
19932
  const { sha256, bytes } = sha256String(content);
@@ -19723,13 +19943,13 @@ async function exportJsonBundle(opts) {
19723
19943
  files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
19724
19944
  };
19725
19945
  const bundle = { manifest, records };
19726
- await writeJsonFile(path30.join(outDirAbs, "manifest.json"), manifest);
19727
- await writeJsonFile(path30.join(outDirAbs, "bundle.json"), bundle);
19946
+ await writeJsonFile(path31.join(outDirAbs, "manifest.json"), manifest);
19947
+ await writeJsonFile(path31.join(outDirAbs, "bundle.json"), bundle);
19728
19948
  }
19729
19949
 
19730
19950
  // src/transfer/export-md.ts
19731
- import path31 from "path";
19732
- import { mkdir as mkdir23, readFile as readFile22, writeFile as writeFile20 } from "fs/promises";
19951
+ import path32 from "path";
19952
+ import { mkdir as mkdir24, readFile as readFile23, writeFile as writeFile21 } from "fs/promises";
19733
19953
  function shouldExclude2(relPosix, includeTranscripts) {
19734
19954
  const parts = relPosix.split("/");
19735
19955
  if (!includeTranscripts && parts[0] === "transcripts") return true;
@@ -19737,18 +19957,18 @@ function shouldExclude2(relPosix, includeTranscripts) {
19737
19957
  }
19738
19958
  async function exportMarkdownBundle(opts) {
19739
19959
  const includeTranscripts = opts.includeTranscripts === true;
19740
- const outDirAbs = path31.resolve(opts.outDir);
19741
- await mkdir23(outDirAbs, { recursive: true });
19742
- const memDirAbs = path31.resolve(opts.memoryDir);
19960
+ const outDirAbs = path32.resolve(opts.outDir);
19961
+ await mkdir24(outDirAbs, { recursive: true });
19962
+ const memDirAbs = path32.resolve(opts.memoryDir);
19743
19963
  const filesAbs = await listFilesRecursive(memDirAbs);
19744
19964
  const manifestFiles = [];
19745
19965
  for (const abs of filesAbs) {
19746
19966
  const relPosix = toPosixRelPath(abs, memDirAbs);
19747
19967
  if (shouldExclude2(relPosix, includeTranscripts)) continue;
19748
- const dstAbs = path31.join(outDirAbs, ...relPosix.split("/"));
19749
- await mkdir23(path31.dirname(dstAbs), { recursive: true });
19750
- const content = await readFile22(abs);
19751
- await writeFile20(dstAbs, content);
19968
+ const dstAbs = path32.join(outDirAbs, ...relPosix.split("/"));
19969
+ await mkdir24(path32.dirname(dstAbs), { recursive: true });
19970
+ const content = await readFile23(abs);
19971
+ await writeFile21(dstAbs, content);
19752
19972
  const { sha256, bytes } = await sha256File(abs);
19753
19973
  manifestFiles.push({ path: relPosix, sha256, bytes });
19754
19974
  }
@@ -19760,12 +19980,12 @@ async function exportMarkdownBundle(opts) {
19760
19980
  includesTranscripts: includeTranscripts,
19761
19981
  files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
19762
19982
  };
19763
- await writeJsonFile(path31.join(outDirAbs, "manifest.json"), manifest);
19983
+ await writeJsonFile(path32.join(outDirAbs, "manifest.json"), manifest);
19764
19984
  }
19765
19985
  async function looksLikeEngramMdExport(fromDir) {
19766
- const dirAbs = path31.resolve(fromDir);
19986
+ const dirAbs = path32.resolve(fromDir);
19767
19987
  try {
19768
- const raw = await readFile22(path31.join(dirAbs, "manifest.json"), "utf-8");
19988
+ const raw = await readFile23(path32.join(dirAbs, "manifest.json"), "utf-8");
19769
19989
  const parsed = JSON.parse(raw);
19770
19990
  return parsed.format === EXPORT_FORMAT && parsed.schemaVersion === EXPORT_SCHEMA_VERSION;
19771
19991
  } catch {
@@ -19774,16 +19994,16 @@ async function looksLikeEngramMdExport(fromDir) {
19774
19994
  }
19775
19995
 
19776
19996
  // src/transfer/backup.ts
19777
- import path32 from "path";
19778
- import { mkdir as mkdir24, readdir as readdir14, rm as rm4 } from "fs/promises";
19997
+ import path33 from "path";
19998
+ import { mkdir as mkdir25, readdir as readdir14, rm as rm4 } from "fs/promises";
19779
19999
  function timestampDirName(now) {
19780
20000
  return now.toISOString().replace(/[:.]/g, "-");
19781
20001
  }
19782
20002
  async function backupMemoryDir(opts) {
19783
- const outDirAbs = path32.resolve(opts.outDir);
19784
- await mkdir24(outDirAbs, { recursive: true });
20003
+ const outDirAbs = path33.resolve(opts.outDir);
20004
+ await mkdir25(outDirAbs, { recursive: true });
19785
20005
  const ts = timestampDirName(/* @__PURE__ */ new Date());
19786
- const backupDir = path32.join(outDirAbs, ts);
20006
+ const backupDir = path33.join(outDirAbs, ts);
19787
20007
  await exportMarkdownBundle({
19788
20008
  memoryDir: opts.memoryDir,
19789
20009
  outDir: backupDir,
@@ -19808,15 +20028,15 @@ async function enforceRetention(outDirAbs, retentionDays) {
19808
20028
  const tsMs = iso ? Date.parse(iso) : NaN;
19809
20029
  if (!Number.isFinite(tsMs)) continue;
19810
20030
  if (tsMs < cutoffMs) {
19811
- await rm4(path32.join(outDirAbs, name), { recursive: true, force: true });
20031
+ await rm4(path33.join(outDirAbs, name), { recursive: true, force: true });
19812
20032
  }
19813
20033
  }
19814
20034
  }
19815
20035
 
19816
20036
  // src/transfer/export-sqlite.ts
19817
- import path33 from "path";
20037
+ import path34 from "path";
19818
20038
  import Database from "better-sqlite3";
19819
- import { readFile as readFile23 } from "fs/promises";
20039
+ import { readFile as readFile24 } from "fs/promises";
19820
20040
 
19821
20041
  // src/transfer/sqlite-schema.ts
19822
20042
  var SQLITE_SCHEMA_VERSION = 1;
@@ -19842,8 +20062,8 @@ function shouldExclude3(relPosix, includeTranscripts) {
19842
20062
  }
19843
20063
  async function exportSqlite(opts) {
19844
20064
  const includeTranscripts = opts.includeTranscripts === true;
19845
- const memDirAbs = path33.resolve(opts.memoryDir);
19846
- const outAbs = path33.resolve(opts.outFile);
20065
+ const memDirAbs = path34.resolve(opts.memoryDir);
20066
+ const outAbs = path34.resolve(opts.outFile);
19847
20067
  const filesAbs = await listFilesRecursive(memDirAbs);
19848
20068
  const db = new Database(outAbs);
19849
20069
  try {
@@ -19864,7 +20084,7 @@ async function exportSqlite(opts) {
19864
20084
  for (const abs of filesAbs) {
19865
20085
  const relPosix = toPosixRelPath(abs, memDirAbs);
19866
20086
  if (shouldExclude3(relPosix, includeTranscripts)) continue;
19867
- const content = await readFile23(abs, "utf-8");
20087
+ const content = await readFile24(abs, "utf-8");
19868
20088
  const { sha256, bytes } = await sha256File(abs);
19869
20089
  rows.push({ rel: relPosix, bytes, sha256, content });
19870
20090
  }
@@ -19875,8 +20095,8 @@ async function exportSqlite(opts) {
19875
20095
  }
19876
20096
 
19877
20097
  // src/transfer/import-json.ts
19878
- import path34 from "path";
19879
- import { mkdir as mkdir25, writeFile as writeFile21 } from "fs/promises";
20098
+ import path35 from "path";
20099
+ import { mkdir as mkdir26, writeFile as writeFile22 } from "fs/promises";
19880
20100
 
19881
20101
  // src/transfer/types.ts
19882
20102
  import { z as z4 } from "zod";
@@ -19909,21 +20129,21 @@ function normalizeForDedupe(s) {
19909
20129
  }
19910
20130
  async function importJsonBundle(opts) {
19911
20131
  const conflict = opts.conflict ?? "skip";
19912
- const fromDirAbs = path34.resolve(opts.fromDir);
19913
- const bundlePath = path34.join(fromDirAbs, "bundle.json");
20132
+ const fromDirAbs = path35.resolve(opts.fromDir);
20133
+ const bundlePath = path35.join(fromDirAbs, "bundle.json");
19914
20134
  const bundle = ExportBundleV1Schema.parse(await readJsonFile(bundlePath));
19915
- const memDirAbs = path34.resolve(opts.targetMemoryDir);
20135
+ const memDirAbs = path35.resolve(opts.targetMemoryDir);
19916
20136
  const written = [];
19917
20137
  let skipped = 0;
19918
20138
  for (const rec of bundle.records) {
19919
20139
  const isWorkspace = rec.path.startsWith("workspace/");
19920
- const targetBase = isWorkspace ? opts.workspaceDir ? path34.resolve(opts.workspaceDir) : null : memDirAbs;
20140
+ const targetBase = isWorkspace ? opts.workspaceDir ? path35.resolve(opts.workspaceDir) : null : memDirAbs;
19921
20141
  if (isWorkspace && !targetBase) {
19922
20142
  skipped += 1;
19923
20143
  continue;
19924
20144
  }
19925
20145
  const relFs = fromPosixRelPath(isWorkspace ? rec.path.replace(/^workspace\//, "") : rec.path);
19926
- const absTarget = path34.join(targetBase, relFs);
20146
+ const absTarget = path35.join(targetBase, relFs);
19927
20147
  const exists3 = await fileExists(absTarget);
19928
20148
  if (exists3) {
19929
20149
  if (conflict === "skip") {
@@ -19947,30 +20167,30 @@ async function importJsonBundle(opts) {
19947
20167
  return { written: 0, skipped };
19948
20168
  }
19949
20169
  for (const w of written) {
19950
- await mkdir25(path34.dirname(w.abs), { recursive: true });
19951
- await writeFile21(w.abs, w.content, "utf-8");
20170
+ await mkdir26(path35.dirname(w.abs), { recursive: true });
20171
+ await writeFile22(w.abs, w.content, "utf-8");
19952
20172
  }
19953
20173
  return { written: written.length, skipped };
19954
20174
  }
19955
20175
  function looksLikeEngramJsonExport(fromDir) {
19956
- const dir = path34.resolve(fromDir);
20176
+ const dir = path35.resolve(fromDir);
19957
20177
  return Promise.all([
19958
- fileExists(path34.join(dir, "manifest.json")),
19959
- fileExists(path34.join(dir, "bundle.json"))
20178
+ fileExists(path35.join(dir, "manifest.json")),
20179
+ fileExists(path35.join(dir, "bundle.json"))
19960
20180
  ]).then(([m, b]) => m && b);
19961
20181
  }
19962
20182
 
19963
20183
  // src/transfer/import-sqlite.ts
19964
- import path35 from "path";
20184
+ import path36 from "path";
19965
20185
  import Database2 from "better-sqlite3";
19966
- import { mkdir as mkdir26, writeFile as writeFile22 } from "fs/promises";
20186
+ import { mkdir as mkdir27, writeFile as writeFile23 } from "fs/promises";
19967
20187
  function normalizeForDedupe2(s) {
19968
20188
  return s.replace(/\s+/g, " ").trim();
19969
20189
  }
19970
20190
  async function importSqlite(opts) {
19971
20191
  const conflict = opts.conflict ?? "skip";
19972
- const memDirAbs = path35.resolve(opts.targetMemoryDir);
19973
- const fromAbs = path35.resolve(opts.fromFile);
20192
+ const memDirAbs = path36.resolve(opts.targetMemoryDir);
20193
+ const fromAbs = path36.resolve(opts.fromFile);
19974
20194
  const db = new Database2(fromAbs, { readonly: true });
19975
20195
  const written = [];
19976
20196
  let skipped = 0;
@@ -19983,7 +20203,7 @@ async function importSqlite(opts) {
19983
20203
  const rows = db.prepare("SELECT path_rel, content FROM files").all();
19984
20204
  for (const r of rows) {
19985
20205
  const relFs = fromPosixRelPath(r.path_rel);
19986
- const absTarget = path35.join(memDirAbs, relFs);
20206
+ const absTarget = path36.join(memDirAbs, relFs);
19987
20207
  const exists3 = await fileExists(absTarget);
19988
20208
  if (exists3) {
19989
20209
  if (conflict === "skip") {
@@ -20008,30 +20228,30 @@ async function importSqlite(opts) {
20008
20228
  }
20009
20229
  if (opts.dryRun) return { written: 0, skipped };
20010
20230
  for (const w of written) {
20011
- await mkdir26(path35.dirname(w.abs), { recursive: true });
20012
- await writeFile22(w.abs, w.content, "utf-8");
20231
+ await mkdir27(path36.dirname(w.abs), { recursive: true });
20232
+ await writeFile23(w.abs, w.content, "utf-8");
20013
20233
  }
20014
20234
  return { written: written.length, skipped };
20015
20235
  }
20016
20236
 
20017
20237
  // src/transfer/import-md.ts
20018
- import path36 from "path";
20019
- import { mkdir as mkdir27, readFile as readFile24, writeFile as writeFile23 } from "fs/promises";
20238
+ import path37 from "path";
20239
+ import { mkdir as mkdir28, readFile as readFile25, writeFile as writeFile24 } from "fs/promises";
20020
20240
  function normalizeForDedupe3(s) {
20021
20241
  return s.replace(/\s+/g, " ").trim();
20022
20242
  }
20023
20243
  async function importMarkdownBundle(opts) {
20024
20244
  const conflict = opts.conflict ?? "skip";
20025
- const fromAbs = path36.resolve(opts.fromDir);
20026
- const targetAbs = path36.resolve(opts.targetMemoryDir);
20245
+ const fromAbs = path37.resolve(opts.fromDir);
20246
+ const targetAbs = path37.resolve(opts.targetMemoryDir);
20027
20247
  const filesAbs = await listFilesRecursive(fromAbs);
20028
20248
  const writes = [];
20029
20249
  let skipped = 0;
20030
20250
  for (const abs of filesAbs) {
20031
20251
  const relPosix = toPosixRelPath(abs, fromAbs);
20032
20252
  if (relPosix === "manifest.json") continue;
20033
- const dstAbs = path36.join(targetAbs, fromPosixRelPath(relPosix));
20034
- const content = await readFile24(abs, "utf-8");
20253
+ const dstAbs = path37.join(targetAbs, fromPosixRelPath(relPosix));
20254
+ const content = await readFile25(abs, "utf-8");
20035
20255
  const exists3 = await fileExists(dstAbs);
20036
20256
  if (exists3) {
20037
20257
  if (conflict === "skip") {
@@ -20053,17 +20273,17 @@ async function importMarkdownBundle(opts) {
20053
20273
  }
20054
20274
  if (opts.dryRun) return { written: 0, skipped };
20055
20275
  for (const w of writes) {
20056
- await mkdir27(path36.dirname(w.abs), { recursive: true });
20057
- await writeFile23(w.abs, w.content, "utf-8");
20276
+ await mkdir28(path37.dirname(w.abs), { recursive: true });
20277
+ await writeFile24(w.abs, w.content, "utf-8");
20058
20278
  }
20059
20279
  return { written: writes.length, skipped };
20060
20280
  }
20061
20281
 
20062
20282
  // src/transfer/autodetect.ts
20063
- import path37 from "path";
20283
+ import path38 from "path";
20064
20284
  import { stat as stat7 } from "fs/promises";
20065
20285
  async function detectImportFormat(fromPath) {
20066
- const abs = path37.resolve(fromPath);
20286
+ const abs = path38.resolve(fromPath);
20067
20287
  let st;
20068
20288
  try {
20069
20289
  st = await stat7(abs);
@@ -20491,8 +20711,8 @@ function gatherCandidates(input, warnings) {
20491
20711
  const record = rec;
20492
20712
  const content = typeof record.content === "string" ? record.content : null;
20493
20713
  if (!content) continue;
20494
- const path46 = typeof record.path === "string" ? record.path : "";
20495
- if (!path46.startsWith("transcripts/") && !path46.includes("/transcripts/")) continue;
20714
+ const path47 = typeof record.path === "string" ? record.path : "";
20715
+ if (!path47.startsWith("transcripts/") && !path47.includes("/transcripts/")) continue;
20496
20716
  rows.push(...parseJsonl(content, warnings));
20497
20717
  }
20498
20718
  return rows;
@@ -20548,8 +20768,8 @@ var openclawReplayNormalizer = {
20548
20768
  };
20549
20769
 
20550
20770
  // src/maintenance/archive-observations.ts
20551
- import path38 from "path";
20552
- import { mkdir as mkdir28, readdir as readdir15, readFile as readFile25, unlink as unlink4, writeFile as writeFile24 } from "fs/promises";
20771
+ import path39 from "path";
20772
+ import { mkdir as mkdir29, readdir as readdir15, readFile as readFile26, unlink as unlink4, writeFile as writeFile25 } from "fs/promises";
20553
20773
  var DATE_FILE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})\.(jsonl|md)$/;
20554
20774
  function normalizeRetentionDays(value) {
20555
20775
  if (!Number.isFinite(value)) return 30;
@@ -20575,8 +20795,8 @@ async function listFilesRecursive2(root, relPrefix = "") {
20575
20795
  return out;
20576
20796
  }
20577
20797
  for (const entry of entries) {
20578
- const rel = relPrefix ? path38.join(relPrefix, entry.name) : entry.name;
20579
- const full = path38.join(root, entry.name);
20798
+ const rel = relPrefix ? path39.join(relPrefix, entry.name) : entry.name;
20799
+ const full = path39.join(root, entry.name);
20580
20800
  if (entry.isDirectory()) {
20581
20801
  out.push(...await listFilesRecursive2(full, rel));
20582
20802
  continue;
@@ -20586,19 +20806,19 @@ async function listFilesRecursive2(root, relPrefix = "") {
20586
20806
  return out;
20587
20807
  }
20588
20808
  async function collectArchiveCandidates(memoryDir, cutoffTimeMs) {
20589
- const roots = ["transcripts", path38.join("state", "tool-usage"), path38.join("summaries", "hourly")];
20809
+ const roots = ["transcripts", path39.join("state", "tool-usage"), path39.join("summaries", "hourly")];
20590
20810
  const out = [];
20591
20811
  for (const relRoot of roots) {
20592
- const absRoot = path38.join(memoryDir, relRoot);
20812
+ const absRoot = path39.join(memoryDir, relRoot);
20593
20813
  const files = await listFilesRecursive2(absRoot);
20594
20814
  for (const fileRel of files) {
20595
- const filename = path38.basename(fileRel);
20815
+ const filename = path39.basename(fileRel);
20596
20816
  const parsedDate = extractDateFromFilename(filename);
20597
20817
  if (!parsedDate) continue;
20598
20818
  if (parsedDate.getTime() >= cutoffTimeMs) continue;
20599
20819
  out.push({
20600
- absolutePath: path38.join(absRoot, fileRel),
20601
- relativePath: path38.join(relRoot, fileRel)
20820
+ absolutePath: path39.join(absRoot, fileRel),
20821
+ relativePath: path39.join(relRoot, fileRel)
20602
20822
  });
20603
20823
  }
20604
20824
  }
@@ -20613,7 +20833,7 @@ async function archiveObservations(options) {
20613
20833
  new Date(now.getTime() - retentionDays * 24 * 60 * 60 * 1e3)
20614
20834
  );
20615
20835
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
20616
- const archiveRoot = path38.join(options.memoryDir, "archive", "observations", stamp);
20836
+ const archiveRoot = path39.join(options.memoryDir, "archive", "observations", stamp);
20617
20837
  const candidates = retentionDays === 0 ? [] : await collectArchiveCandidates(
20618
20838
  options.memoryDir,
20619
20839
  cutoffDayStartUtc
@@ -20622,13 +20842,13 @@ async function archiveObservations(options) {
20622
20842
  let archivedBytes = 0;
20623
20843
  const archivedRelativePaths = [];
20624
20844
  if (!dryRun && candidates.length > 0) {
20625
- await mkdir28(archiveRoot, { recursive: true });
20845
+ await mkdir29(archiveRoot, { recursive: true });
20626
20846
  for (const candidate of candidates) {
20627
- const archivePath = path38.join(archiveRoot, candidate.relativePath);
20628
- const archiveDir = path38.dirname(archivePath);
20629
- await mkdir28(archiveDir, { recursive: true });
20630
- const raw = await readFile25(candidate.absolutePath);
20631
- await writeFile24(archivePath, raw);
20847
+ const archivePath = path39.join(archiveRoot, candidate.relativePath);
20848
+ const archiveDir = path39.dirname(archivePath);
20849
+ await mkdir29(archiveDir, { recursive: true });
20850
+ const raw = await readFile26(candidate.absolutePath);
20851
+ await writeFile25(archivePath, raw);
20632
20852
  await unlink4(candidate.absolutePath);
20633
20853
  archivedFiles += 1;
20634
20854
  archivedBytes += raw.byteLength;
@@ -20649,12 +20869,12 @@ async function archiveObservations(options) {
20649
20869
  }
20650
20870
 
20651
20871
  // src/maintenance/rebuild-observations.ts
20652
- import path40 from "path";
20653
- import { readdir as readdir16, readFile as readFile27 } from "fs/promises";
20872
+ import path41 from "path";
20873
+ import { readdir as readdir16, readFile as readFile28 } from "fs/promises";
20654
20874
 
20655
20875
  // src/maintenance/observation-ledger-utils.ts
20656
- import path39 from "path";
20657
- import { mkdir as mkdir29, readFile as readFile26, writeFile as writeFile25 } from "fs/promises";
20876
+ import path40 from "path";
20877
+ import { mkdir as mkdir30, readFile as readFile27, writeFile as writeFile26 } from "fs/promises";
20658
20878
  function toHourBucketIso(timestamp) {
20659
20879
  const normalized = /(?:Z|[+-]\d{2}:\d{2})$/u.test(timestamp) ? timestamp : `${timestamp}Z`;
20660
20880
  const ms = Date.parse(normalized);
@@ -20665,17 +20885,17 @@ function toHourBucketIso(timestamp) {
20665
20885
  }
20666
20886
  async function backupAndWriteRebuiltObservations(options) {
20667
20887
  const stamp = options.now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
20668
- const archiveRoot = path39.join(options.memoryDir, "archive", "observations", stamp);
20669
- let backupPath = path39.join(
20888
+ const archiveRoot = path40.join(options.memoryDir, "archive", "observations", stamp);
20889
+ let backupPath = path40.join(
20670
20890
  archiveRoot,
20671
20891
  "state",
20672
20892
  "observation-ledger",
20673
20893
  "rebuilt-observations.jsonl"
20674
20894
  );
20675
20895
  try {
20676
- const existing = await readFile26(options.outputPath, "utf-8");
20677
- await mkdir29(path39.dirname(backupPath), { recursive: true });
20678
- await writeFile25(backupPath, existing, "utf-8");
20896
+ const existing = await readFile27(options.outputPath, "utf-8");
20897
+ await mkdir30(path40.dirname(backupPath), { recursive: true });
20898
+ await writeFile26(backupPath, existing, "utf-8");
20679
20899
  } catch (err) {
20680
20900
  const code = err.code;
20681
20901
  if (code && code === "ENOENT") {
@@ -20691,8 +20911,8 @@ async function backupAndWriteRebuiltObservations(options) {
20691
20911
  rebuiltAt
20692
20912
  })
20693
20913
  );
20694
- await mkdir29(path39.dirname(options.outputPath), { recursive: true });
20695
- await writeFile25(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
20914
+ await mkdir30(path40.dirname(options.outputPath), { recursive: true });
20915
+ await writeFile26(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
20696
20916
  ` : "", "utf-8");
20697
20917
  return backupPath;
20698
20918
  }
@@ -20714,7 +20934,7 @@ async function listTranscriptFiles(root) {
20714
20934
  for (const entry of entries) {
20715
20935
  if (entry.name === "." || entry.name === "..") continue;
20716
20936
  if (entry.isSymbolicLink()) continue;
20717
- const full = path40.join(root, entry.name);
20937
+ const full = path41.join(root, entry.name);
20718
20938
  if (entry.isDirectory()) {
20719
20939
  out.push(...await listTranscriptFiles(full));
20720
20940
  continue;
@@ -20774,8 +20994,8 @@ function buildLedgerRows(linesByFile) {
20774
20994
  async function rebuildObservations(options) {
20775
20995
  const dryRun = options.dryRun !== false;
20776
20996
  const now = options.now ?? /* @__PURE__ */ new Date();
20777
- const transcriptsRoot = path40.join(options.memoryDir, "transcripts");
20778
- const outputPath = path40.join(
20997
+ const transcriptsRoot = path41.join(options.memoryDir, "transcripts");
20998
+ const outputPath = path41.join(
20779
20999
  options.memoryDir,
20780
21000
  "state",
20781
21001
  "observation-ledger",
@@ -20785,7 +21005,7 @@ async function rebuildObservations(options) {
20785
21005
  const contents = [];
20786
21006
  for (const file of transcriptFiles) {
20787
21007
  try {
20788
- contents.push(await readFile27(file, "utf-8"));
21008
+ contents.push(await readFile28(file, "utf-8"));
20789
21009
  } catch {
20790
21010
  }
20791
21011
  }
@@ -20811,8 +21031,8 @@ async function rebuildObservations(options) {
20811
21031
  }
20812
21032
 
20813
21033
  // src/maintenance/migrate-observations.ts
20814
- import path41 from "path";
20815
- import { readdir as readdir17, readFile as readFile28 } from "fs/promises";
21034
+ import path42 from "path";
21035
+ import { readdir as readdir17, readFile as readFile29 } from "fs/promises";
20816
21036
  function toNonNegativeInt(value) {
20817
21037
  if (typeof value !== "number" || !Number.isFinite(value)) return null;
20818
21038
  const normalized = Math.floor(value);
@@ -20875,16 +21095,16 @@ async function listLegacyObservationFiles(root) {
20875
21095
  async function migrateObservations(options) {
20876
21096
  const dryRun = options.dryRun !== false;
20877
21097
  const now = options.now ?? /* @__PURE__ */ new Date();
20878
- const ledgerRoot = path41.join(options.memoryDir, "state", "observation-ledger");
20879
- const outputPath = path41.join(ledgerRoot, "rebuilt-observations.jsonl");
21098
+ const ledgerRoot = path42.join(options.memoryDir, "state", "observation-ledger");
21099
+ const outputPath = path42.join(ledgerRoot, "rebuilt-observations.jsonl");
20880
21100
  const legacyFiles = await listLegacyObservationFiles(ledgerRoot);
20881
- const sourceRelativePaths = legacyFiles.map((name) => path41.join("state", "observation-ledger", name));
21101
+ const sourceRelativePaths = legacyFiles.map((name) => path42.join("state", "observation-ledger", name));
20882
21102
  const byKey = /* @__PURE__ */ new Map();
20883
21103
  let parsedRows = 0;
20884
21104
  let malformedLines = 0;
20885
21105
  for (const file of legacyFiles) {
20886
- const full = path41.join(ledgerRoot, file);
20887
- const raw = await readFile28(full, "utf-8");
21106
+ const full = path42.join(ledgerRoot, file);
21107
+ const raw = await readFile29(full, "utf-8");
20888
21108
  for (const line of raw.split("\n")) {
20889
21109
  if (!line.trim()) continue;
20890
21110
  let parsed;
@@ -21073,10 +21293,10 @@ var defaultCommandRunner = (command, args, options) => {
21073
21293
 
21074
21294
  // src/network/webdav.ts
21075
21295
  import { createReadStream } from "fs";
21076
- import { mkdir as mkdir30, readdir as readdir18, realpath as realpath2, stat as stat9 } from "fs/promises";
21296
+ import { mkdir as mkdir31, readdir as readdir18, realpath as realpath2, stat as stat9 } from "fs/promises";
21077
21297
  import { createServer } from "http";
21078
21298
  import { timingSafeEqual } from "crypto";
21079
- import path42 from "path";
21299
+ import path43 from "path";
21080
21300
  import { pipeline } from "stream/promises";
21081
21301
  import { URL as URL2 } from "url";
21082
21302
  function hostToUrlAuthority(host) {
@@ -21112,10 +21332,10 @@ var WebDavServer = class _WebDavServer {
21112
21332
  const allowedRoots = [];
21113
21333
  const aliasSet = /* @__PURE__ */ new Set();
21114
21334
  for (const dir of options.allowlistDirs) {
21115
- const resolved = path42.resolve(dir);
21116
- await mkdir30(resolved, { recursive: true });
21335
+ const resolved = path43.resolve(dir);
21336
+ await mkdir31(resolved, { recursive: true });
21117
21337
  const canonical = await realpath2(resolved);
21118
- const alias = path42.basename(canonical) || "root";
21338
+ const alias = path43.basename(canonical) || "root";
21119
21339
  if (aliasSet.has(alias)) {
21120
21340
  throw new Error(`duplicate webdav allowlist alias: ${alias}`);
21121
21341
  }
@@ -21271,7 +21491,7 @@ var WebDavServer = class _WebDavServer {
21271
21491
  if (decodedPath.includes("\0")) {
21272
21492
  return { ok: false, code: 400, message: "invalid path" };
21273
21493
  }
21274
- const normalized = path42.posix.normalize(decodedPath);
21494
+ const normalized = path43.posix.normalize(decodedPath);
21275
21495
  const segments = normalized.split("/").filter((segment) => segment.length > 0);
21276
21496
  if (segments.length === 0) {
21277
21497
  return { ok: false, code: 403, message: "root listing is not allowed" };
@@ -21285,7 +21505,7 @@ var WebDavServer = class _WebDavServer {
21285
21505
  if (relative.some((segment) => segment === ".." || segment.includes("\\"))) {
21286
21506
  return { ok: false, code: 403, message: "path traversal is not allowed" };
21287
21507
  }
21288
- const candidate = path42.resolve(root.absolute, ...relative);
21508
+ const candidate = path43.resolve(root.absolute, ...relative);
21289
21509
  if (!this.isPathInside(root.absolute, candidate)) {
21290
21510
  return { ok: false, code: 403, message: "path escaped allowlist" };
21291
21511
  }
@@ -21363,10 +21583,10 @@ var WebDavServer = class _WebDavServer {
21363
21583
  }
21364
21584
  isPathInside(root, target) {
21365
21585
  if (target === root) return true;
21366
- if (root === path42.parse(root).root) {
21586
+ if (root === path43.parse(root).root) {
21367
21587
  return target.startsWith(root);
21368
21588
  }
21369
- return target.startsWith(`${root}${path42.sep}`);
21589
+ return target.startsWith(`${root}${path43.sep}`);
21370
21590
  }
21371
21591
  };
21372
21592
  function xmlEscape(value) {
@@ -21377,8 +21597,8 @@ function toEncodedHref(pathname) {
21377
21597
  }
21378
21598
 
21379
21599
  // src/compat/checks.ts
21380
- import { access as access2, readFile as readFile29 } from "fs/promises";
21381
- import path43 from "path";
21600
+ import { access as access2, readFile as readFile30 } from "fs/promises";
21601
+ import path44 from "path";
21382
21602
  import { spawn as spawn4 } from "child_process";
21383
21603
  var REQUIRED_HOOKS = ["before_agent_start", "agent_end"];
21384
21604
  function isSafeCommandToken(command) {
@@ -21560,13 +21780,13 @@ function compareVersions(a, b) {
21560
21780
  async function runCompatChecks(options) {
21561
21781
  const checks = [];
21562
21782
  const runner = options.runner ?? defaultRunner;
21563
- const pluginJsonPath = path43.join(options.repoRoot, "openclaw.plugin.json");
21564
- const packageJsonPath = path43.join(options.repoRoot, "package.json");
21565
- const indexPath = path43.join(options.repoRoot, "src", "index.ts");
21783
+ const pluginJsonPath = path44.join(options.repoRoot, "openclaw.plugin.json");
21784
+ const packageJsonPath = path44.join(options.repoRoot, "package.json");
21785
+ const indexPath = path44.join(options.repoRoot, "src", "index.ts");
21566
21786
  let pluginRaw = "";
21567
21787
  let pluginManifestPresent = false;
21568
21788
  try {
21569
- pluginRaw = await readFile29(pluginJsonPath, "utf-8");
21789
+ pluginRaw = await readFile30(pluginJsonPath, "utf-8");
21570
21790
  pluginManifestPresent = true;
21571
21791
  checks.push({
21572
21792
  id: "plugin-manifest-present",
@@ -21615,7 +21835,7 @@ async function runCompatChecks(options) {
21615
21835
  let packageRaw = "";
21616
21836
  let packageJsonPresent = false;
21617
21837
  try {
21618
- packageRaw = await readFile29(packageJsonPath, "utf-8");
21838
+ packageRaw = await readFile30(packageJsonPath, "utf-8");
21619
21839
  packageJsonPresent = true;
21620
21840
  } catch {
21621
21841
  checks.push({
@@ -21686,7 +21906,7 @@ async function runCompatChecks(options) {
21686
21906
  }
21687
21907
  try {
21688
21908
  await access2(indexPath);
21689
- const indexRaw = await readFile29(indexPath, "utf-8");
21909
+ const indexRaw = await readFile30(indexPath, "utf-8");
21690
21910
  const structuralSource = stripCommentsAndStrings(indexRaw);
21691
21911
  const hooks = parseHookRegistrations(indexRaw);
21692
21912
  const missingHooks = REQUIRED_HOOKS.filter((hook) => !hooks.has(hook));
@@ -21902,6 +22122,171 @@ async function runTierMigrateCliCommand(orchestrator, options = {}) {
21902
22122
  limit: options.limit
21903
22123
  });
21904
22124
  }
22125
+ function effectivePolicyValuesForVersion(values, config) {
22126
+ const candidate = {
22127
+ recencyWeight: values.recencyWeight ?? config.recencyWeight,
22128
+ lifecyclePromoteHeatThreshold: values.lifecyclePromoteHeatThreshold ?? config.lifecyclePromoteHeatThreshold,
22129
+ lifecycleStaleDecayThreshold: values.lifecycleStaleDecayThreshold ?? config.lifecycleStaleDecayThreshold,
22130
+ cronRecallInstructionHeavyTokenCap: values.cronRecallInstructionHeavyTokenCap ?? config.cronRecallInstructionHeavyTokenCap
22131
+ };
22132
+ const normalized = sanitizeRuntimePolicyValues(candidate, {
22133
+ maxStaleDecayThreshold: config.lifecycleArchiveDecayThreshold
22134
+ });
22135
+ return {
22136
+ recencyWeight: normalized.recencyWeight ?? config.recencyWeight,
22137
+ lifecyclePromoteHeatThreshold: normalized.lifecyclePromoteHeatThreshold ?? config.lifecyclePromoteHeatThreshold,
22138
+ lifecycleStaleDecayThreshold: normalized.lifecycleStaleDecayThreshold ?? config.lifecycleStaleDecayThreshold,
22139
+ cronRecallInstructionHeavyTokenCap: normalized.cronRecallInstructionHeavyTokenCap ?? config.cronRecallInstructionHeavyTokenCap
22140
+ };
22141
+ }
22142
+ function policyVersionForValues(values, config) {
22143
+ const normalized = effectivePolicyValuesForVersion(values, config);
22144
+ return createHash9("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
22145
+ }
22146
+ async function readRuntimePolicySnapshot2(config, fileName) {
22147
+ const filePath = path45.join(config.memoryDir, "state", fileName);
22148
+ const snapshot = await readRuntimePolicySnapshot(filePath, {
22149
+ maxStaleDecayThreshold: config.lifecycleArchiveDecayThreshold
22150
+ });
22151
+ if (!snapshot) return null;
22152
+ return {
22153
+ version: snapshot.version,
22154
+ updatedAt: snapshot.updatedAt,
22155
+ values: snapshot.values,
22156
+ sourceAdjustmentCount: Math.max(0, Math.floor(snapshot.sourceAdjustmentCount))
22157
+ };
22158
+ }
22159
+ function parseSinceDurationMs(since) {
22160
+ const trimmed = since.trim().toLowerCase();
22161
+ const match = trimmed.match(/^(\d+)\s*([mhd])$/);
22162
+ if (!match) {
22163
+ throw new Error(`invalid --since value: ${since} (expected formats like 30m, 12h, 7d)`);
22164
+ }
22165
+ const amount = Number.parseInt(match[1] ?? "0", 10);
22166
+ const unit = match[2];
22167
+ if (!Number.isFinite(amount) || amount <= 0) {
22168
+ throw new Error(`invalid --since value: ${since}`);
22169
+ }
22170
+ if (unit === "m") return amount * 60 * 1e3;
22171
+ if (unit === "h") return amount * 60 * 60 * 1e3;
22172
+ return amount * 24 * 60 * 60 * 1e3;
22173
+ }
22174
+ function resolvePolicySignalNamespaces(orchestrator) {
22175
+ const names = /* @__PURE__ */ new Set([orchestrator.config.defaultNamespace]);
22176
+ if (orchestrator.config.namespacesEnabled) {
22177
+ names.add(orchestrator.config.sharedNamespace);
22178
+ for (const policy of orchestrator.config.namespacePolicies) {
22179
+ if (policy?.name) names.add(policy.name);
22180
+ }
22181
+ }
22182
+ return [...names];
22183
+ }
22184
+ async function readBehaviorSignalsForNamespaces(orchestrator, limitPerNamespace) {
22185
+ const namespaces = resolvePolicySignalNamespaces(orchestrator);
22186
+ const merged = [];
22187
+ for (const namespace of namespaces) {
22188
+ const storage = await orchestrator.getStorage(namespace);
22189
+ const events = await storage.readBehaviorSignals(limitPerNamespace);
22190
+ merged.push(...events);
22191
+ }
22192
+ return merged;
22193
+ }
22194
+ function summarizeTopSignals(signals, cutoffIso, topN = 5) {
22195
+ const cutoffMs = cutoffIso ? Date.parse(cutoffIso) : Number.NEGATIVE_INFINITY;
22196
+ const grouped = /* @__PURE__ */ new Map();
22197
+ for (const signal of signals) {
22198
+ const ts = Date.parse(signal.timestamp);
22199
+ if (Number.isFinite(cutoffMs) && (!Number.isFinite(ts) || ts < cutoffMs)) continue;
22200
+ const key = `${signal.signalType}:${signal.direction}`;
22201
+ const existing = grouped.get(key);
22202
+ if (existing) {
22203
+ existing.count += 1;
22204
+ if (signal.timestamp > existing.lastSeenAt) {
22205
+ existing.lastSeenAt = signal.timestamp;
22206
+ }
22207
+ } else {
22208
+ grouped.set(key, {
22209
+ signalType: signal.signalType,
22210
+ direction: signal.direction,
22211
+ count: 1,
22212
+ lastSeenAt: signal.timestamp
22213
+ });
22214
+ }
22215
+ }
22216
+ return [...grouped.values()].sort((a, b) => b.count - a.count || b.lastSeenAt.localeCompare(a.lastSeenAt)).slice(0, Math.max(1, topN));
22217
+ }
22218
+ async function runPolicyStatusCliCommand(orchestrator) {
22219
+ const now = /* @__PURE__ */ new Date();
22220
+ const current = await readRuntimePolicySnapshot2(orchestrator.config, "policy-runtime.json");
22221
+ const previous = await readRuntimePolicySnapshot2(orchestrator.config, "policy-runtime.prev.json");
22222
+ const signals = await readBehaviorSignalsForNamespaces(orchestrator, 1e3);
22223
+ const defaultWindowMs = Math.max(0, orchestrator.config.behaviorLoopLearningWindowDays) * 24 * 60 * 60 * 1e3;
22224
+ const cutoffIso = defaultWindowMs > 0 ? new Date(now.getTime() - defaultWindowMs).toISOString() : void 0;
22225
+ return {
22226
+ generatedAt: now.toISOString(),
22227
+ autoTuneEnabled: orchestrator.config.behaviorLoopAutoTuneEnabled,
22228
+ current: current ? {
22229
+ ...current,
22230
+ policyVersion: policyVersionForValues(current.values, orchestrator.config)
22231
+ } : null,
22232
+ previous: previous ? {
22233
+ ...previous,
22234
+ policyVersion: policyVersionForValues(previous.values, orchestrator.config)
22235
+ } : null,
22236
+ topContributingSignals: summarizeTopSignals(signals, cutoffIso)
22237
+ };
22238
+ }
22239
+ async function runPolicyDiffCliCommand(orchestrator, options = {}) {
22240
+ const since = options.since?.trim() || "7d";
22241
+ const sinceMs = parseSinceDurationMs(since);
22242
+ const sinceIso = new Date(Date.now() - sinceMs).toISOString();
22243
+ const current = await readRuntimePolicySnapshot2(orchestrator.config, "policy-runtime.json");
22244
+ const previous = await readRuntimePolicySnapshot2(orchestrator.config, "policy-runtime.prev.json");
22245
+ const currentValues = current?.values ?? {};
22246
+ const previousValues = previous?.values ?? {};
22247
+ const parameterKeys = /* @__PURE__ */ new Set([
22248
+ ...Object.keys(currentValues),
22249
+ ...Object.keys(previousValues)
22250
+ ]);
22251
+ const deltas = [];
22252
+ for (const parameter of parameterKeys) {
22253
+ const previousRaw = previousValues[parameter];
22254
+ const nextRaw = currentValues[parameter];
22255
+ const previousValue = typeof previousRaw === "number" ? previousRaw : null;
22256
+ const nextValue = typeof nextRaw === "number" ? nextRaw : null;
22257
+ if (previousValue === nextValue) continue;
22258
+ deltas.push({
22259
+ parameter,
22260
+ previousValue,
22261
+ nextValue,
22262
+ delta: (nextValue ?? 0) - (previousValue ?? 0),
22263
+ evidenceCount: current?.sourceAdjustmentCount ?? 0
22264
+ });
22265
+ }
22266
+ deltas.sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta) || a.parameter.localeCompare(b.parameter));
22267
+ const signals = await readBehaviorSignalsForNamespaces(orchestrator, 1e3);
22268
+ return {
22269
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22270
+ since,
22271
+ sinceIso,
22272
+ currentPolicyVersion: current ? policyVersionForValues(current.values, orchestrator.config) : null,
22273
+ previousPolicyVersion: previous ? policyVersionForValues(previous.values, orchestrator.config) : null,
22274
+ deltas,
22275
+ topContributingSignals: summarizeTopSignals(signals, sinceIso)
22276
+ };
22277
+ }
22278
+ async function runPolicyRollbackCliCommand(orchestrator) {
22279
+ const rolledBack = await orchestrator.rollbackBehaviorRuntimePolicy();
22280
+ const current = await readRuntimePolicySnapshot2(orchestrator.config, "policy-runtime.json");
22281
+ return {
22282
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22283
+ rolledBack,
22284
+ current: current ? {
22285
+ ...current,
22286
+ policyVersion: policyVersionForValues(current.values, orchestrator.config)
22287
+ } : null
22288
+ };
22289
+ }
21905
22290
  function incrementCounter(target, key) {
21906
22291
  const normalized = key && key.length > 0 ? key : "unknown";
21907
22292
  target[normalized] = (target[normalized] ?? 0) + 1;
@@ -22218,7 +22603,7 @@ async function withTimeout(promise, timeoutMs, timeoutMessage) {
22218
22603
  }
22219
22604
  async function runReplayCliCommand(orchestrator, options) {
22220
22605
  const extractionIdleTimeoutMs = Number.isFinite(options.extractionIdleTimeoutMs) ? Math.max(1e3, Math.floor(options.extractionIdleTimeoutMs)) : 15 * 6e4;
22221
- const inputRaw = await readFile30(options.inputPath, "utf-8");
22606
+ const inputRaw = await readFile31(options.inputPath, "utf-8");
22222
22607
  const registry = buildReplayNormalizerRegistry([
22223
22608
  openclawReplayNormalizer,
22224
22609
  claudeReplayNormalizer,
@@ -22283,7 +22668,7 @@ async function runReplayCliCommand(orchestrator, options) {
22283
22668
  async function getPluginVersion() {
22284
22669
  try {
22285
22670
  const pkgPath = new URL("../package.json", import.meta.url);
22286
- const raw = await readFile30(pkgPath, "utf-8");
22671
+ const raw = await readFile31(pkgPath, "utf-8");
22287
22672
  const parsed = JSON.parse(raw);
22288
22673
  return parsed.version ?? "unknown";
22289
22674
  } catch {
@@ -22302,14 +22687,14 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
22302
22687
  const ns = (namespace ?? "").trim();
22303
22688
  if (!ns) return orchestrator.config.memoryDir;
22304
22689
  if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
22305
- const candidate = path44.join(orchestrator.config.memoryDir, "namespaces", ns);
22690
+ const candidate = path45.join(orchestrator.config.memoryDir, "namespaces", ns);
22306
22691
  if (ns === orchestrator.config.defaultNamespace) {
22307
22692
  return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
22308
22693
  }
22309
22694
  return candidate;
22310
22695
  }
22311
22696
  async function readAllMemoryFiles(memoryDir) {
22312
- const roots = [path44.join(memoryDir, "facts"), path44.join(memoryDir, "corrections")];
22697
+ const roots = [path45.join(memoryDir, "facts"), path45.join(memoryDir, "corrections")];
22313
22698
  const out = [];
22314
22699
  const walk = async (dir) => {
22315
22700
  let entries;
@@ -22320,14 +22705,14 @@ async function readAllMemoryFiles(memoryDir) {
22320
22705
  }
22321
22706
  for (const entry of entries) {
22322
22707
  const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
22323
- const fullPath = path44.join(dir, entryName);
22708
+ const fullPath = path45.join(dir, entryName);
22324
22709
  if (entry.isDirectory()) {
22325
22710
  await walk(fullPath);
22326
22711
  continue;
22327
22712
  }
22328
22713
  if (!entry.isFile() || !entryName.endsWith(".md")) continue;
22329
22714
  try {
22330
- const raw = await readFile30(fullPath, "utf-8");
22715
+ const raw = await readFile31(fullPath, "utf-8");
22331
22716
  const parsed = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
22332
22717
  if (!parsed) continue;
22333
22718
  const fmRaw = parsed[1];
@@ -22621,6 +23006,23 @@ function registerCli(api, orchestrator) {
22621
23006
  console.log(JSON.stringify(summary, null, 2));
22622
23007
  console.log("OK");
22623
23008
  });
23009
+ cmd.command("policy-status").description("Show runtime behavior-loop policy status and top contributing signals").action(async () => {
23010
+ const status = await runPolicyStatusCliCommand(orchestrator);
23011
+ console.log(JSON.stringify(status, null, 2));
23012
+ console.log("OK");
23013
+ });
23014
+ cmd.command("policy-diff").description("Show runtime policy deltas and evidence since a relative duration (default: 7d)").option("--since <window>", "Relative duration window like 30m, 12h, 7d", "7d").action(async (...args) => {
23015
+ const options = args[0] ?? {};
23016
+ const since = typeof options.since === "string" ? options.since : "7d";
23017
+ const report = await runPolicyDiffCliCommand(orchestrator, { since });
23018
+ console.log(JSON.stringify(report, null, 2));
23019
+ console.log("OK");
23020
+ });
23021
+ cmd.command("policy-rollback").description("Roll back runtime behavior policy to the previous snapshot").action(async () => {
23022
+ const report = await runPolicyRollbackCliCommand(orchestrator);
23023
+ console.log(JSON.stringify(report, null, 2));
23024
+ console.log("OK");
23025
+ });
22624
23026
  cmd.command("action-audit").description("Show namespace-aware memory action policy outcomes").option("--namespace <name>", "Filter to a single namespace").option("--limit <n>", "Max events to read per namespace", "200").action(async (...args) => {
22625
23027
  const options = args[0] ?? {};
22626
23028
  const limitRaw = parseInt(String(options.limit ?? "200"), 10);
@@ -23135,7 +23537,7 @@ function registerCli(api, orchestrator) {
23135
23537
  }
23136
23538
  });
23137
23539
  cmd.command("identity").description("Show agent identity reflections").action(async () => {
23138
- const workspaceDir = path44.join(process.env.HOME ?? "~", ".openclaw", "workspace");
23540
+ const workspaceDir = path45.join(process.env.HOME ?? "~", ".openclaw", "workspace");
23139
23541
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
23140
23542
  if (!identity) {
23141
23543
  console.log("No identity file found.");
@@ -23358,8 +23760,8 @@ function registerCli(api, orchestrator) {
23358
23760
  const options = args[0] ?? {};
23359
23761
  const threadId = options.thread;
23360
23762
  const top = parseInt(options.top ?? "10", 10);
23361
- const memoryDir = path44.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
23362
- const threading = new ThreadingManager(path44.join(memoryDir, "threads"));
23763
+ const memoryDir = path45.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
23764
+ const threading = new ThreadingManager(path45.join(memoryDir, "threads"));
23363
23765
  if (threadId) {
23364
23766
  const thread = await threading.loadThread(threadId);
23365
23767
  if (!thread) {
@@ -23532,16 +23934,16 @@ function parseDuration(duration) {
23532
23934
  }
23533
23935
 
23534
23936
  // src/index.ts
23535
- import { readFile as readFile31, writeFile as writeFile26 } from "fs/promises";
23937
+ import { readFile as readFile32, writeFile as writeFile27 } from "fs/promises";
23536
23938
  import { readFileSync as readFileSync4 } from "fs";
23537
- import path45 from "path";
23939
+ import path46 from "path";
23538
23940
  import os5 from "os";
23539
23941
  var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
23540
23942
  function loadPluginConfigFromFile() {
23541
23943
  try {
23542
23944
  const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
23543
23945
  const homeDir = process.env.HOME ?? os5.homedir();
23544
- const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path45.join(homeDir, ".openclaw", "openclaw.json");
23946
+ const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path46.join(homeDir, ".openclaw", "openclaw.json");
23545
23947
  const content = readFileSync4(configPath, "utf-8");
23546
23948
  const config = JSON.parse(content);
23547
23949
  const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
@@ -23749,11 +24151,11 @@ Use this context naturally when relevant. Never quote or expose this memory cont
23749
24151
  );
23750
24152
  async function ensureHourlySummaryCron(api2) {
23751
24153
  const jobId = "engram-hourly-summary";
23752
- const cronFilePath = path45.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
24154
+ const cronFilePath = path46.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
23753
24155
  try {
23754
24156
  let jobsData = { version: 1, jobs: [] };
23755
24157
  try {
23756
- const content = await readFile31(cronFilePath, "utf-8");
24158
+ const content = await readFile32(cronFilePath, "utf-8");
23757
24159
  jobsData = JSON.parse(content);
23758
24160
  } catch {
23759
24161
  }
@@ -23790,7 +24192,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
23790
24192
  state: {}
23791
24193
  };
23792
24194
  jobsData.jobs.push(newJob);
23793
- await writeFile26(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
24195
+ await writeFile27(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
23794
24196
  log.info("auto-registered hourly summary cron job");
23795
24197
  } catch (err) {
23796
24198
  log.error("failed to auto-register hourly summary cron job:", err);