@joshuaswarren/openclaw-engram 8.3.78 → 8.3.80

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();
@@ -9049,18 +9235,18 @@ function sanitizeNonNegativeInt(value) {
9049
9235
  if (!Number.isFinite(value)) return 0;
9050
9236
  return Math.max(0, Math.floor(value));
9051
9237
  }
9052
- function parseIsoMs(value) {
9238
+ function parseIsoMs2(value) {
9053
9239
  if (!value) return 0;
9054
9240
  const ms = Date.parse(value);
9055
9241
  return Number.isFinite(ms) ? ms : 0;
9056
9242
  }
9057
9243
  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);
9244
+ const existingObservedMs = parseIsoMs2(existing.lastObservedAt);
9245
+ const incomingObservedMs = parseIsoMs2(incoming.lastObservedAt);
9246
+ const existingTriggeredMs = parseIsoMs2(existing.lastTriggeredAt);
9247
+ const incomingTriggeredMs = parseIsoMs2(incoming.lastTriggeredAt);
9248
+ const existingResetMs = parseIsoMs2(existing.lastResetAt);
9249
+ const incomingResetMs = parseIsoMs2(incoming.lastResetAt);
9064
9250
  const observedAt = incomingObservedMs >= existingObservedMs ? incoming.lastObservedAt : existing.lastObservedAt;
9065
9251
  const triggeredAt = incomingTriggeredMs >= existingTriggeredMs ? incoming.lastTriggeredAt : existing.lastTriggeredAt;
9066
9252
  const incomingIsNewer = incomingObservedMs >= existingObservedMs;
@@ -9818,6 +10004,10 @@ function buildInstructionHeavyQuery(prompt, tokenCap, maxChars) {
9818
10004
  if (compact.length <= maxChars) return compact;
9819
10005
  return compact.slice(0, maxChars).trim();
9820
10006
  }
10007
+ function clampInstructionHeavyTokenCap(value) {
10008
+ if (!Number.isFinite(value)) return 8;
10009
+ return Math.max(8, Math.round(value));
10010
+ }
9821
10011
  function buildStandardQuery(prompt, maxChars) {
9822
10012
  const trimmed = collapseWhitespace(prompt);
9823
10013
  if (trimmed.length <= maxChars) return trimmed;
@@ -9836,7 +10026,7 @@ function buildRecallQueryPolicy(prompt, sessionKey, cfg) {
9836
10026
  }
9837
10027
  const promptShape = classifyRecallPromptShape(prompt);
9838
10028
  const maxChars = Math.max(120, cfg.cronRecallNormalizedQueryMaxChars);
9839
- const tokenCap = Math.max(8, cfg.cronRecallInstructionHeavyTokenCap);
10029
+ const tokenCap = clampInstructionHeavyTokenCap(cfg.cronRecallInstructionHeavyTokenCap);
9840
10030
  const retrievalQuery = promptShape === "instruction_heavy" ? buildInstructionHeavyQuery(prompt, tokenCap, maxChars) : buildStandardQuery(prompt, maxChars);
9841
10031
  const skipConversationRecall = cfg.cronConversationRecallMode === "never" ? true : cfg.cronConversationRecallMode === "always" ? false : promptShape === "instruction_heavy";
9842
10032
  const retrievalBudgetMode = promptShape === "instruction_heavy" ? "minimal" : "full";
@@ -10870,180 +11060,6 @@ var TmtBuilder = class {
10870
11060
  }
10871
11061
  };
10872
11062
 
10873
- // src/lifecycle.ts
10874
- var DEFAULT_POLICY = {
10875
- promoteHeatThreshold: 0.55,
10876
- staleDecayThreshold: 0.65,
10877
- archiveDecayThreshold: 0.85,
10878
- protectedCategories: ["decision", "principle", "commitment", "preference"]
10879
- };
10880
- function clamp01(value) {
10881
- if (!Number.isFinite(value)) return 0;
10882
- if (value < 0) return 0;
10883
- if (value > 1) return 1;
10884
- return value;
10885
- }
10886
- function parseIsoMs2(value) {
10887
- if (!value) return null;
10888
- const ms = Date.parse(value);
10889
- return Number.isFinite(ms) ? ms : null;
10890
- }
10891
- function daysSince(value, nowMs) {
10892
- const ts = parseIsoMs2(value);
10893
- if (ts === null) return 365;
10894
- return Math.max(0, (nowMs - ts) / 864e5);
10895
- }
10896
- function confidenceTierWeight(frontmatter) {
10897
- switch (frontmatter.confidenceTier) {
10898
- case "explicit":
10899
- return 1;
10900
- case "implied":
10901
- return 0.8;
10902
- case "inferred":
10903
- return 0.6;
10904
- case "speculative":
10905
- return 0.35;
10906
- default:
10907
- return clamp01(frontmatter.confidence ?? 0.5);
10908
- }
10909
- }
10910
- function accessWeight(accessCount) {
10911
- const raw = accessCount ?? 0;
10912
- if (raw <= 0) return 0;
10913
- return clamp01(Math.log1p(raw) / Math.log1p(20));
10914
- }
10915
- function recencyWeight(frontmatter, nowMs) {
10916
- const lastTouch = frontmatter.lastAccessed ?? frontmatter.updated ?? frontmatter.created;
10917
- const ageDays = daysSince(lastTouch, nowMs);
10918
- return clamp01(1 - ageDays / 90);
10919
- }
10920
- function feedbackWeight(signals) {
10921
- const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);
10922
- return clamp01((raw + 1) / 2);
10923
- }
10924
- function boundedFeedbackScore(signals) {
10925
- const raw = (signals?.feedbackScore ?? 0) + (signals?.actionPriorScore ?? 0);
10926
- if (!Number.isFinite(raw)) return 0;
10927
- if (raw < -1) return -1;
10928
- if (raw > 1) return 1;
10929
- return raw;
10930
- }
10931
- function isProtectedMemory(frontmatter, policy) {
10932
- return frontmatter.policyClass === "protected" || policy.protectedCategories.includes(frontmatter.category);
10933
- }
10934
- function resolveLifecycleState(frontmatter) {
10935
- if (frontmatter.status === "archived") return "archived";
10936
- return frontmatter.lifecycleState ?? "candidate";
10937
- }
10938
- function computeHeat(memory, now, signals) {
10939
- const frontmatter = memory.frontmatter;
10940
- if (frontmatter.status === "archived") return 0;
10941
- const inputs = computeLifecycleValueInputs(memory, now, signals);
10942
- const score = inputs.confidence * 0.25 + inputs.access * 0.3 + inputs.recency * 0.2 + inputs.importance * 0.15 + inputs.feedback * 0.1 - inputs.disputedPenalty;
10943
- return clamp01(score);
10944
- }
10945
- function computeLifecycleValueInputs(memory, now, signals) {
10946
- const frontmatter = memory.frontmatter;
10947
- const nowMs = now.getTime();
10948
- return {
10949
- confidence: confidenceTierWeight(frontmatter),
10950
- access: accessWeight(frontmatter.accessCount),
10951
- recency: recencyWeight(frontmatter, nowMs),
10952
- importance: clamp01(frontmatter.importance?.score ?? 0.5),
10953
- feedback: feedbackWeight(signals),
10954
- disputedPenalty: frontmatter.verificationState === "disputed" ? 0.2 : 0
10955
- };
10956
- }
10957
- function computeDecay(memory, now, signals) {
10958
- const frontmatter = memory.frontmatter;
10959
- if (frontmatter.status === "archived") return 1;
10960
- const nowMs = now.getTime();
10961
- const ageDays = daysSince(frontmatter.updated ?? frontmatter.created, nowMs);
10962
- const staleAccessDays = daysSince(frontmatter.lastAccessed, nowMs);
10963
- const ageRisk = clamp01(ageDays / 180);
10964
- const staleAccessRisk = clamp01(staleAccessDays / 120);
10965
- const confidenceRisk = 1 - confidenceTierWeight(frontmatter);
10966
- const feedbackRisk = clamp01((boundedFeedbackScore(signals) * -1 + 1) / 2);
10967
- const heat = computeHeat(memory, now, signals);
10968
- const score = ageRisk * 0.3 + staleAccessRisk * 0.25 + confidenceRisk * 0.2 + feedbackRisk * 0.1 + (1 - heat) * 0.15;
10969
- return clamp01(score);
10970
- }
10971
- function toTerminalDisputedState(currentState) {
10972
- if (currentState === "archived") return "archived";
10973
- return "stale";
10974
- }
10975
- function isActiveEligible(verificationState) {
10976
- return verificationState === "user_confirmed" || verificationState === "system_inferred";
10977
- }
10978
- function decideLifecycleTransition(memory, policy, now, signals) {
10979
- const mergedPolicy = { ...DEFAULT_POLICY, ...policy };
10980
- const frontmatter = memory.frontmatter;
10981
- const currentState = resolveLifecycleState(frontmatter);
10982
- const heatScore = computeHeat(memory, now, signals);
10983
- const decayScore = computeDecay(memory, now, signals);
10984
- const protectedMemory = isProtectedMemory(frontmatter, mergedPolicy);
10985
- if (currentState === "archived") {
10986
- return {
10987
- currentState,
10988
- nextState: "archived",
10989
- heatScore,
10990
- decayScore,
10991
- changed: false,
10992
- reason: "archived_is_terminal"
10993
- };
10994
- }
10995
- if (frontmatter.verificationState === "disputed") {
10996
- const nextState = toTerminalDisputedState(currentState);
10997
- return {
10998
- currentState,
10999
- nextState,
11000
- heatScore,
11001
- decayScore,
11002
- changed: nextState !== currentState,
11003
- reason: "disputed_memories_do_not_promote_to_active"
11004
- };
11005
- }
11006
- if (decayScore >= mergedPolicy.archiveDecayThreshold && !protectedMemory) {
11007
- return {
11008
- currentState,
11009
- nextState: "archived",
11010
- heatScore,
11011
- decayScore,
11012
- changed: true,
11013
- reason: "decay_exceeded_archive_threshold"
11014
- };
11015
- }
11016
- if (decayScore >= mergedPolicy.staleDecayThreshold) {
11017
- return {
11018
- currentState,
11019
- nextState: "stale",
11020
- heatScore,
11021
- decayScore,
11022
- changed: currentState !== "stale",
11023
- reason: "decay_exceeded_stale_threshold"
11024
- };
11025
- }
11026
- if (heatScore >= mergedPolicy.promoteHeatThreshold) {
11027
- const nextState = isActiveEligible(frontmatter.verificationState) ? "active" : "validated";
11028
- return {
11029
- currentState,
11030
- nextState,
11031
- heatScore,
11032
- decayScore,
11033
- changed: currentState !== nextState,
11034
- reason: "heat_exceeded_promote_threshold"
11035
- };
11036
- }
11037
- return {
11038
- currentState,
11039
- nextState: currentState,
11040
- heatScore,
11041
- decayScore,
11042
- changed: false,
11043
- reason: "no_transition"
11044
- };
11045
- }
11046
-
11047
11063
  // src/temporal-index.ts
11048
11064
  import * as fs2 from "fs";
11049
11065
  import * as path16 from "path";
@@ -13121,6 +13137,154 @@ var RoutingRulesStore = class {
13121
13137
  }
13122
13138
  };
13123
13139
 
13140
+ // src/policy-runtime.ts
13141
+ import path26 from "path";
13142
+ import { mkdir as mkdir19, readFile as readFile18, rename as rename3, writeFile as writeFile17 } from "fs/promises";
13143
+ var RUNTIME_POLICY_VERSION = 1;
13144
+ var RUNTIME_POLICY_FILE = "policy-runtime.json";
13145
+ var RUNTIME_POLICY_PREV_FILE = "policy-runtime.prev.json";
13146
+ function sanitizeRuntimePolicyValues(values, options) {
13147
+ const out = {};
13148
+ if (typeof values.recencyWeight === "number") {
13149
+ out.recencyWeight = clamp01(values.recencyWeight);
13150
+ }
13151
+ if (typeof values.lifecyclePromoteHeatThreshold === "number") {
13152
+ out.lifecyclePromoteHeatThreshold = clampLifecycleThreshold(values.lifecyclePromoteHeatThreshold);
13153
+ }
13154
+ if (typeof values.lifecycleStaleDecayThreshold === "number") {
13155
+ const staleDecayThreshold = clampLifecycleThreshold(values.lifecycleStaleDecayThreshold);
13156
+ const maxStaleDecayThreshold = typeof options?.maxStaleDecayThreshold === "number" ? clampLifecycleThreshold(options.maxStaleDecayThreshold) : 1;
13157
+ out.lifecycleStaleDecayThreshold = Math.min(staleDecayThreshold, maxStaleDecayThreshold);
13158
+ }
13159
+ if (typeof values.cronRecallInstructionHeavyTokenCap === "number") {
13160
+ out.cronRecallInstructionHeavyTokenCap = clampInstructionHeavyTokenCap(
13161
+ values.cronRecallInstructionHeavyTokenCap
13162
+ );
13163
+ }
13164
+ return out;
13165
+ }
13166
+ function isRuntimeParameter(parameter) {
13167
+ return parameter === "recencyWeight" || parameter === "lifecyclePromoteHeatThreshold" || parameter === "lifecycleStaleDecayThreshold" || parameter === "cronRecallInstructionHeavyTokenCap";
13168
+ }
13169
+ async function readSnapshot(filePath) {
13170
+ try {
13171
+ const raw = await readFile18(filePath, "utf-8");
13172
+ const parsed = JSON.parse(raw);
13173
+ 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) {
13174
+ return null;
13175
+ }
13176
+ return {
13177
+ version: parsed.version,
13178
+ updatedAt: parsed.updatedAt,
13179
+ values: sanitizeRuntimePolicyValues(parsed.values),
13180
+ sourceAdjustmentCount: parsed.sourceAdjustmentCount
13181
+ };
13182
+ } catch {
13183
+ return null;
13184
+ }
13185
+ }
13186
+ async function writeSnapshotAtomic(filePath, snapshot) {
13187
+ const tempPath = `${filePath}.tmp`;
13188
+ await mkdir19(path26.dirname(filePath), { recursive: true });
13189
+ await writeFile17(tempPath, `${JSON.stringify(snapshot, null, 2)}
13190
+ `, "utf-8");
13191
+ await rename3(tempPath, filePath);
13192
+ }
13193
+ var PolicyRuntimeManager = class {
13194
+ constructor(memoryDir, config) {
13195
+ this.memoryDir = memoryDir;
13196
+ this.config = config;
13197
+ const stateDir2 = path26.join(memoryDir, "state");
13198
+ this.runtimePath = path26.join(stateDir2, RUNTIME_POLICY_FILE);
13199
+ this.runtimePrevPath = path26.join(stateDir2, RUNTIME_POLICY_PREV_FILE);
13200
+ }
13201
+ runtimePath;
13202
+ runtimePrevPath;
13203
+ async loadRuntimeValues() {
13204
+ const snapshot = await readSnapshot(this.runtimePath);
13205
+ if (!snapshot) return null;
13206
+ return sanitizeRuntimePolicyValues(snapshot.values, {
13207
+ maxStaleDecayThreshold: this.config.lifecycleArchiveDecayThreshold
13208
+ });
13209
+ }
13210
+ async rollback() {
13211
+ const previous = await readSnapshot(this.runtimePrevPath);
13212
+ if (!previous) return false;
13213
+ await writeSnapshotAtomic(this.runtimePath, {
13214
+ ...previous,
13215
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13216
+ });
13217
+ return true;
13218
+ }
13219
+ async applyFromBehaviorState(state) {
13220
+ const adjustmentCount = state.adjustments.length;
13221
+ if (adjustmentCount === 0) {
13222
+ return { applied: false, rolledBack: false, values: await this.loadRuntimeValues(), reason: "no_adjustments" };
13223
+ }
13224
+ const protectedSet = /* @__PURE__ */ new Set([
13225
+ ...this.config.behaviorLoopProtectedParams,
13226
+ ...state.protectedParams
13227
+ ]);
13228
+ const existing = await readSnapshot(this.runtimePath);
13229
+ const candidate = {
13230
+ recencyWeight: existing?.values.recencyWeight ?? this.config.recencyWeight,
13231
+ lifecyclePromoteHeatThreshold: existing?.values.lifecyclePromoteHeatThreshold ?? this.config.lifecyclePromoteHeatThreshold,
13232
+ lifecycleStaleDecayThreshold: existing?.values.lifecycleStaleDecayThreshold ?? this.config.lifecycleStaleDecayThreshold,
13233
+ cronRecallInstructionHeavyTokenCap: existing?.values.cronRecallInstructionHeavyTokenCap ?? this.config.cronRecallInstructionHeavyTokenCap
13234
+ };
13235
+ for (const adjustment of state.adjustments) {
13236
+ if (!isRuntimeParameter(adjustment.parameter)) {
13237
+ let rolledBack = false;
13238
+ if (existing) {
13239
+ await writeSnapshotAtomic(this.runtimePath, existing);
13240
+ rolledBack = true;
13241
+ } else {
13242
+ rolledBack = await this.rollback();
13243
+ }
13244
+ return {
13245
+ applied: false,
13246
+ rolledBack,
13247
+ values: await this.loadRuntimeValues(),
13248
+ reason: `invalid_parameter:${adjustment.parameter}`
13249
+ };
13250
+ }
13251
+ if (protectedSet.has(adjustment.parameter)) {
13252
+ continue;
13253
+ }
13254
+ if (!Number.isFinite(adjustment.nextValue)) {
13255
+ let rolledBack = false;
13256
+ if (existing) {
13257
+ await writeSnapshotAtomic(this.runtimePath, existing);
13258
+ rolledBack = true;
13259
+ } else {
13260
+ rolledBack = await this.rollback();
13261
+ }
13262
+ return {
13263
+ applied: false,
13264
+ rolledBack,
13265
+ values: await this.loadRuntimeValues(),
13266
+ reason: `invalid_value:${adjustment.parameter}`
13267
+ };
13268
+ }
13269
+ candidate[adjustment.parameter] = adjustment.nextValue;
13270
+ }
13271
+ const sanitized = sanitizeRuntimePolicyValues(candidate, {
13272
+ maxStaleDecayThreshold: this.config.lifecycleArchiveDecayThreshold
13273
+ });
13274
+ if (existing) {
13275
+ await writeSnapshotAtomic(this.runtimePrevPath, existing);
13276
+ }
13277
+ const nextSnapshot = {
13278
+ version: RUNTIME_POLICY_VERSION,
13279
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13280
+ values: sanitized,
13281
+ sourceAdjustmentCount: adjustmentCount
13282
+ };
13283
+ await writeSnapshotAtomic(this.runtimePath, nextSnapshot);
13284
+ return { applied: true, rolledBack: false, values: sanitized, reason: "applied" };
13285
+ }
13286
+ };
13287
+
13124
13288
  // src/behavior-signals.ts
13125
13289
  import { createHash as createHash5 } from "crypto";
13126
13290
  function normalizeSignalText(input) {
@@ -13312,11 +13476,11 @@ function mergeGraphExpandedResults(primary, expanded) {
13312
13476
  return Array.from(mergedByPath.values());
13313
13477
  }
13314
13478
  function graphPathRelativeToStorage(storageDir, candidatePath) {
13315
- const absolutePath = path26.isAbsolute(candidatePath) ? candidatePath : path26.resolve(storageDir, candidatePath);
13316
- const rel = path26.relative(storageDir, absolutePath);
13479
+ const absolutePath = path27.isAbsolute(candidatePath) ? candidatePath : path27.resolve(storageDir, candidatePath);
13480
+ const rel = path27.relative(storageDir, absolutePath);
13317
13481
  if (!rel || rel === ".") return null;
13318
13482
  if (rel.startsWith("..")) return null;
13319
- return rel.split(path26.sep).join("/");
13483
+ return rel.split(path27.sep).join("/");
13320
13484
  }
13321
13485
  function normalizeGraphActivationScore(score) {
13322
13486
  const bounded = Number.isFinite(score) && score > 0 ? score : 0;
@@ -13391,7 +13555,7 @@ function buildMemoryPathById(allMemsForGraph, storageDir) {
13391
13555
  for (const mem of allMemsForGraph ?? []) {
13392
13556
  const id = mem.frontmatter.id;
13393
13557
  if (!id) continue;
13394
- pathById.set(id, path26.relative(storageDir, mem.path));
13558
+ pathById.set(id, path27.relative(storageDir, mem.path));
13395
13559
  }
13396
13560
  return pathById;
13397
13561
  }
@@ -13399,7 +13563,7 @@ function appendMemoryToGraphContext(options) {
13399
13563
  if (!Array.isArray(options.allMemsForGraph)) return;
13400
13564
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
13401
13565
  options.allMemsForGraph.push({
13402
- path: path26.join(options.storageDir, options.memoryRelPath),
13566
+ path: path27.join(options.storageDir, options.memoryRelPath),
13403
13567
  content: options.content,
13404
13568
  frontmatter: {
13405
13569
  id: options.memoryId,
@@ -13419,15 +13583,15 @@ function resolvePersistedMemoryRelativePath(options) {
13419
13583
  const persisted = options.pathById.get(options.memoryId);
13420
13584
  if (persisted) return persisted;
13421
13585
  if (options.category === "correction") {
13422
- return path26.join("corrections", `${options.memoryId}.md`);
13586
+ return path27.join("corrections", `${options.memoryId}.md`);
13423
13587
  }
13424
13588
  const idParts = options.memoryId.split("-");
13425
13589
  const maybeTimestamp = Number(idParts[1]);
13426
13590
  if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
13427
13591
  const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
13428
- return path26.join("facts", day, `${options.memoryId}.md`);
13592
+ return path27.join("facts", day, `${options.memoryId}.md`);
13429
13593
  }
13430
- return path26.join("facts", `${options.memoryId}.md`);
13594
+ return path27.join("facts", `${options.memoryId}.md`);
13431
13595
  }
13432
13596
  var Orchestrator = class _Orchestrator {
13433
13597
  storage;
@@ -13486,6 +13650,8 @@ var Orchestrator = class _Orchestrator {
13486
13650
  lastRecallFailureLogAtMs = 0;
13487
13651
  lastRecallFailureAtMs = 0;
13488
13652
  suppressedRecallFailures = 0;
13653
+ policyRuntime;
13654
+ runtimePolicyValues = null;
13489
13655
  // Initialization gate: recall() awaits this before proceeding
13490
13656
  initPromise = null;
13491
13657
  resolveInit = null;
@@ -13535,7 +13701,7 @@ var Orchestrator = class _Orchestrator {
13535
13701
  this.compounding = config.compoundingEnabled ? new CompoundingEngine(config) : void 0;
13536
13702
  this.buffer = new SmartBuffer(config, this.storage);
13537
13703
  this.transcript = new TranscriptManager(config);
13538
- this.conversationIndexDir = path26.join(config.memoryDir, "conversation-index", "chunks");
13704
+ this.conversationIndexDir = path27.join(config.memoryDir, "conversation-index", "chunks");
13539
13705
  this.modelRegistry = new ModelRegistry(config.memoryDir);
13540
13706
  this.relevance = new RelevanceStore(config.memoryDir);
13541
13707
  this.negatives = new NegativeExampleStore(config.memoryDir);
@@ -13547,11 +13713,12 @@ var Orchestrator = class _Orchestrator {
13547
13713
  bands: config.sessionObserverBands ?? []
13548
13714
  });
13549
13715
  this.embeddingFallback = new EmbeddingFallback(config);
13716
+ this.policyRuntime = new PolicyRuntimeManager(config.memoryDir, config);
13550
13717
  this.summarizer = new HourlySummarizer(config, config.gatewayConfig, this.modelRegistry, this.transcript);
13551
13718
  this.localLlm = new LocalLlmClient(config, this.modelRegistry);
13552
13719
  this.extraction = new ExtractionEngine(config, this.localLlm, config.gatewayConfig, this.modelRegistry);
13553
13720
  this.threading = new ThreadingManager(
13554
- path26.join(config.memoryDir, "threads"),
13721
+ path27.join(config.memoryDir, "threads"),
13555
13722
  config.threadingGapMinutes
13556
13723
  );
13557
13724
  this.tmtBuilder = new TmtBuilder(config.memoryDir, {
@@ -13579,6 +13746,27 @@ var Orchestrator = class _Orchestrator {
13579
13746
  }
13580
13747
  return this.boxBuilders.get(dir);
13581
13748
  }
13749
+ effectiveRecencyWeight() {
13750
+ return applyRuntimeRetrievalPolicy(
13751
+ { recencyWeight: this.config.recencyWeight },
13752
+ this.runtimePolicyValues
13753
+ ).recencyWeight;
13754
+ }
13755
+ effectiveCronRecallInstructionHeavyTokenCap() {
13756
+ return this.runtimePolicyValues?.cronRecallInstructionHeavyTokenCap ?? this.config.cronRecallInstructionHeavyTokenCap;
13757
+ }
13758
+ effectiveLifecycleThresholds() {
13759
+ const archiveDecayThreshold = this.config.lifecycleArchiveDecayThreshold;
13760
+ const staleDecayThreshold = Math.min(
13761
+ this.runtimePolicyValues?.lifecycleStaleDecayThreshold ?? this.config.lifecycleStaleDecayThreshold,
13762
+ archiveDecayThreshold
13763
+ );
13764
+ return {
13765
+ promoteHeatThreshold: this.runtimePolicyValues?.lifecyclePromoteHeatThreshold ?? this.config.lifecyclePromoteHeatThreshold,
13766
+ staleDecayThreshold,
13767
+ archiveDecayThreshold
13768
+ };
13769
+ }
13582
13770
  routeEngineOptions() {
13583
13771
  const allowedNamespaces = this.config.namespacesEnabled ? Array.from(
13584
13772
  /* @__PURE__ */ new Set([
@@ -13687,8 +13875,9 @@ var Orchestrator = class _Orchestrator {
13687
13875
  await this.lastRecall.load();
13688
13876
  await this.tierMigrationStatus.load();
13689
13877
  await this.sessionObserver.load();
13878
+ this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
13690
13879
  if (this.config.factDeduplicationEnabled) {
13691
- const stateDir2 = path26.join(this.config.memoryDir, "state");
13880
+ const stateDir2 = path27.join(this.config.memoryDir, "state");
13692
13881
  this.contentHashIndex = new ContentHashIndex(stateDir2);
13693
13882
  await this.contentHashIndex.load();
13694
13883
  log.info(`content-hash dedup: loaded ${this.contentHashIndex.size} hashes`);
@@ -13726,7 +13915,7 @@ var Orchestrator = class _Orchestrator {
13726
13915
  if (available) {
13727
13916
  log.info(`Conversation index QMD: available ${this.conversationQmd.debugStatus()}`);
13728
13917
  const collectionState = await this.conversationQmd.ensureCollection(
13729
- path26.join(this.config.memoryDir, "conversation-index")
13918
+ path27.join(this.config.memoryDir, "conversation-index")
13730
13919
  );
13731
13920
  if (collectionState === "missing") {
13732
13921
  this.config.conversationIndexEnabled = false;
@@ -13762,6 +13951,16 @@ var Orchestrator = class _Orchestrator {
13762
13951
  this.resolveInit = null;
13763
13952
  }
13764
13953
  }
13954
+ async applyBehaviorRuntimePolicy(state) {
13955
+ const result = await this.policyRuntime.applyFromBehaviorState(state);
13956
+ this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
13957
+ return result;
13958
+ }
13959
+ async rollbackBehaviorRuntimePolicy() {
13960
+ const rolledBack = await this.policyRuntime.rollback();
13961
+ this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
13962
+ return rolledBack;
13963
+ }
13765
13964
  async maybeRunFileHygiene() {
13766
13965
  const hygiene = this.config.fileHygiene;
13767
13966
  if (!hygiene?.enabled) return;
@@ -13770,12 +13969,12 @@ var Orchestrator = class _Orchestrator {
13770
13969
  this.lastFileHygieneRunAtMs = now;
13771
13970
  if (hygiene.rotateEnabled) {
13772
13971
  for (const rel of hygiene.rotatePaths) {
13773
- const abs = path26.isAbsolute(rel) ? rel : path26.join(this.config.workspaceDir, rel);
13972
+ const abs = path27.isAbsolute(rel) ? rel : path27.join(this.config.workspaceDir, rel);
13774
13973
  try {
13775
- const raw = await readFile18(abs, "utf-8");
13974
+ const raw = await readFile19(abs, "utf-8");
13776
13975
  if (raw.length > hygiene.rotateMaxBytes) {
13777
- const archiveDir = path26.join(this.config.workspaceDir, hygiene.archiveDir);
13778
- const base = path26.basename(abs);
13976
+ const archiveDir = path27.join(this.config.workspaceDir, hygiene.archiveDir);
13977
+ const base = path27.basename(abs);
13779
13978
  const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
13780
13979
  const { newContent } = await rotateMarkdownFileToArchive({
13781
13980
  filePath: abs,
@@ -13783,7 +13982,7 @@ var Orchestrator = class _Orchestrator {
13783
13982
  archivePrefix: prefix,
13784
13983
  keepTailChars: hygiene.rotateKeepTailChars
13785
13984
  });
13786
- await writeFile17(abs, newContent, "utf-8");
13985
+ await writeFile18(abs, newContent, "utf-8");
13787
13986
  }
13788
13987
  } catch {
13789
13988
  }
@@ -13800,8 +13999,8 @@ var Orchestrator = class _Orchestrator {
13800
13999
  log.warn(w.message);
13801
14000
  }
13802
14001
  if (hygiene.warningsLogEnabled && warnings.length > 0) {
13803
- const fp = path26.join(this.config.memoryDir, hygiene.warningsLogPath);
13804
- await mkdir19(path26.dirname(fp), { recursive: true });
14002
+ const fp = path27.join(this.config.memoryDir, hygiene.warningsLogPath);
14003
+ await mkdir20(path27.dirname(fp), { recursive: true });
13805
14004
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
13806
14005
  const block = `
13807
14006
 
@@ -13810,11 +14009,11 @@ var Orchestrator = class _Orchestrator {
13810
14009
  ` + warnings.map((w) => `- ${w.message}`).join("\n") + "\n";
13811
14010
  let existing = "";
13812
14011
  try {
13813
- existing = await readFile18(fp, "utf-8");
14012
+ existing = await readFile19(fp, "utf-8");
13814
14013
  } catch {
13815
14014
  existing = "# Engram File Hygiene Warnings\n";
13816
14015
  }
13817
- await writeFile17(fp, existing + block, "utf-8");
14016
+ await writeFile18(fp, existing + block, "utf-8");
13818
14017
  }
13819
14018
  }
13820
14019
  }
@@ -13890,9 +14089,9 @@ var Orchestrator = class _Orchestrator {
13890
14089
  }
13891
14090
  async getLastGraphRecallSnapshot(namespace) {
13892
14091
  const storage = await this.getStorage(namespace);
13893
- const snapshotPath = path26.join(storage.dir, "state", "last_graph_recall.json");
14092
+ const snapshotPath = path27.join(storage.dir, "state", "last_graph_recall.json");
13894
14093
  try {
13895
- const raw = await readFile18(snapshotPath, "utf-8");
14094
+ const raw = await readFile19(snapshotPath, "utf-8");
13896
14095
  const parsed = JSON.parse(raw);
13897
14096
  if (!parsed || typeof parsed !== "object") return null;
13898
14097
  return {
@@ -13961,7 +14160,7 @@ ${r.snippet.trim()}
13961
14160
  const entries = await readdir11(dir, { withFileTypes: true });
13962
14161
  let total = 0;
13963
14162
  for (const entry of entries) {
13964
- const fullPath = path26.join(dir, entry.name);
14163
+ const fullPath = path27.join(dir, entry.name);
13965
14164
  if (entry.isDirectory()) {
13966
14165
  total += await this.countConversationChunkDocs(fullPath);
13967
14166
  continue;
@@ -14277,7 +14476,7 @@ ${r.snippet.trim()}
14277
14476
  const seedRelativePaths = seedCandidates.map((result) => graphPathRelativeToStorage(storage.dir, result.path)).filter((value) => typeof value === "string" && value.length > 0);
14278
14477
  if (seedRelativePaths.length === 0) continue;
14279
14478
  const seedRecallScore = seedCandidates.reduce((max, item) => Math.max(max, item.score), 0);
14280
- seedPaths.push(...seedRelativePaths.map((rel) => path26.join(storage.dir, rel)));
14479
+ seedPaths.push(...seedRelativePaths.map((rel) => path27.join(storage.dir, rel)));
14281
14480
  const seedSet = new Set(seedRelativePaths);
14282
14481
  const expanded = await this.graphIndexFor(storage).spreadingActivation(
14283
14482
  seedRelativePaths,
@@ -14286,7 +14485,7 @@ ${r.snippet.trim()}
14286
14485
  if (expanded.length === 0) continue;
14287
14486
  for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
14288
14487
  if (seedSet.has(candidate.path)) continue;
14289
- const memoryPath = path26.resolve(storage.dir, candidate.path);
14488
+ const memoryPath = path27.resolve(storage.dir, candidate.path);
14290
14489
  const memory = await storage.readMemoryByPath(memoryPath);
14291
14490
  if (!memory) continue;
14292
14491
  if (isArtifactMemoryPath(memory.path)) continue;
@@ -14309,7 +14508,7 @@ ${r.snippet.trim()}
14309
14508
  path: memory.path,
14310
14509
  score,
14311
14510
  namespace,
14312
- seed: path26.resolve(storage.dir, candidate.seed),
14511
+ seed: path27.resolve(storage.dir, candidate.seed),
14313
14512
  hopDepth: candidate.hopDepth,
14314
14513
  decayedWeight: candidate.decayedWeight,
14315
14514
  graphType: candidate.graphType
@@ -14324,8 +14523,8 @@ ${r.snippet.trim()}
14324
14523
  }
14325
14524
  async recordLastGraphRecallSnapshot(options) {
14326
14525
  try {
14327
- const snapshotPath = path26.join(options.storage.dir, "state", "last_graph_recall.json");
14328
- await mkdir19(path26.dirname(snapshotPath), { recursive: true });
14526
+ const snapshotPath = path27.join(options.storage.dir, "state", "last_graph_recall.json");
14527
+ await mkdir20(path27.dirname(snapshotPath), { recursive: true });
14329
14528
  const now = (/* @__PURE__ */ new Date()).toISOString();
14330
14529
  const totalSeedCount = options.seedPaths.length;
14331
14530
  const totalExpandedCount = options.expandedPaths.length;
@@ -14342,7 +14541,7 @@ ${r.snippet.trim()}
14342
14541
  seeds,
14343
14542
  expanded
14344
14543
  };
14345
- await writeFile17(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
14544
+ await writeFile18(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
14346
14545
  } catch (err) {
14347
14546
  log.debug(`last graph recall write failed: ${err}`);
14348
14547
  }
@@ -14355,7 +14554,7 @@ ${r.snippet.trim()}
14355
14554
  const queryPolicy = buildRecallQueryPolicy(prompt, sessionKey, {
14356
14555
  cronRecallPolicyEnabled: this.config.cronRecallPolicyEnabled,
14357
14556
  cronRecallNormalizedQueryMaxChars: this.config.cronRecallNormalizedQueryMaxChars,
14358
- cronRecallInstructionHeavyTokenCap: this.config.cronRecallInstructionHeavyTokenCap,
14557
+ cronRecallInstructionHeavyTokenCap: this.effectiveCronRecallInstructionHeavyTokenCap(),
14359
14558
  cronConversationRecallMode: this.config.cronConversationRecallMode
14360
14559
  });
14361
14560
  const retrievalQuery = queryPolicy.retrievalQuery || prompt;
@@ -15349,7 +15548,7 @@ _Context: ${topQuestion.context}_`);
15349
15548
  };
15350
15549
  this.tierMigrationInFlight = true;
15351
15550
  try {
15352
- const coldStorage = new StorageManager(path26.join(storage.dir, "cold"));
15551
+ const coldStorage = new StorageManager(path27.join(storage.dir, "cold"));
15353
15552
  const [hotMemories, coldMemories] = await Promise.all([
15354
15553
  storage.readAllMemories(),
15355
15554
  coldStorage.readAllMemories()
@@ -15943,7 +16142,7 @@ _Context: ${topQuestion.context}_`);
15943
16142
  const allMems = allMemsForGraph ?? [];
15944
16143
  for (const m of allMems) {
15945
16144
  if (m.frontmatter.entityRef === entityRef) {
15946
- const rel = path26.relative(storage.dir, m.path);
16145
+ const rel = path27.relative(storage.dir, m.path);
15947
16146
  if (rel !== memoryRelPath) entitySiblings.push(rel);
15948
16147
  }
15949
16148
  }
@@ -16445,10 +16644,11 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
16445
16644
  let updatedCount = 0;
16446
16645
  let disputedCount = 0;
16447
16646
  let evaluatedCount = 0;
16647
+ const thresholds = this.effectiveLifecycleThresholds();
16448
16648
  const policy = {
16449
- promoteHeatThreshold: this.config.lifecyclePromoteHeatThreshold,
16450
- staleDecayThreshold: this.config.lifecycleStaleDecayThreshold,
16451
- archiveDecayThreshold: this.config.lifecycleArchiveDecayThreshold,
16649
+ promoteHeatThreshold: thresholds.promoteHeatThreshold,
16650
+ staleDecayThreshold: thresholds.staleDecayThreshold,
16651
+ archiveDecayThreshold: thresholds.archiveDecayThreshold,
16452
16652
  protectedCategories: this.config.lifecycleProtectedCategories
16453
16653
  };
16454
16654
  const actionPriors = await this.buildLifecycleActionPriors();
@@ -16494,15 +16694,15 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
16494
16694
  staleRatio: total > 0 ? countsByState.stale / total : 0,
16495
16695
  disputedRatio: total > 0 ? disputedCount / total : 0,
16496
16696
  policy: {
16497
- promoteHeatThreshold: this.config.lifecyclePromoteHeatThreshold,
16498
- staleDecayThreshold: this.config.lifecycleStaleDecayThreshold,
16499
- archiveDecayThreshold: this.config.lifecycleArchiveDecayThreshold,
16697
+ promoteHeatThreshold: thresholds.promoteHeatThreshold,
16698
+ staleDecayThreshold: thresholds.staleDecayThreshold,
16699
+ archiveDecayThreshold: thresholds.archiveDecayThreshold,
16500
16700
  protectedCategories: this.config.lifecycleProtectedCategories
16501
16701
  }
16502
16702
  };
16503
- const metricsPath = path26.join(this.storage.dir, "state", "lifecycle-metrics.json");
16504
- await mkdir19(path26.dirname(metricsPath), { recursive: true });
16505
- await writeFile17(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
16703
+ const metricsPath = path27.join(this.storage.dir, "state", "lifecycle-metrics.json");
16704
+ await mkdir20(path27.dirname(metricsPath), { recursive: true });
16705
+ await writeFile18(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
16506
16706
  }
16507
16707
  /**
16508
16708
  * Archive old, low-importance, rarely-accessed facts (v6.0).
@@ -16768,7 +16968,7 @@ ${lines.join("\n\n")}`;
16768
16968
  if (hits.length === 0) return [];
16769
16969
  const results = [];
16770
16970
  for (const hit of hits) {
16771
- const fullPath = path26.isAbsolute(hit.path) ? hit.path : path26.join(this.config.memoryDir, hit.path);
16971
+ const fullPath = path27.isAbsolute(hit.path) ? hit.path : path27.join(this.config.memoryDir, hit.path);
16772
16972
  const memory = await this.storage.readMemoryByPath(fullPath);
16773
16973
  if (!memory) continue;
16774
16974
  results.push({
@@ -17027,6 +17227,7 @@ ${lines.join("\n\n")}`;
17027
17227
  }
17028
17228
  let lifecycleFilteredCount = 0;
17029
17229
  const boosted = [];
17230
+ const recencyWeight2 = this.effectiveRecencyWeight();
17030
17231
  for (const r of results) {
17031
17232
  const memory = memoryByPath.get(r.path);
17032
17233
  let score = r.score;
@@ -17038,13 +17239,13 @@ ${lines.join("\n\n")}`;
17038
17239
  lifecycleFilteredCount += 1;
17039
17240
  continue;
17040
17241
  }
17041
- if (this.config.recencyWeight > 0) {
17242
+ if (recencyWeight2 > 0) {
17042
17243
  const createdAt = new Date(memory.frontmatter.created).getTime();
17043
17244
  const ageMs = now - createdAt;
17044
17245
  const ageDays = ageMs / (1e3 * 60 * 60 * 24);
17045
17246
  const halfLifeDays = 7;
17046
17247
  const recencyScore = Math.pow(0.5, ageDays / halfLifeDays);
17047
- score = score * (1 - this.config.recencyWeight) + recencyScore * this.config.recencyWeight;
17248
+ score = score * (1 - recencyWeight2) + recencyScore * recencyWeight2;
17048
17249
  }
17049
17250
  if (this.config.boostAccessCount && memory.frontmatter.accessCount) {
17050
17251
  const accessBoost = Math.log10(memory.frontmatter.accessCount + 1) / 3;
@@ -17225,8 +17426,8 @@ ${lines.join("\n\n")}`;
17225
17426
  }
17226
17427
  namespaceFromStorageDir(storageDir) {
17227
17428
  if (!this.config.namespacesEnabled) return this.config.defaultNamespace;
17228
- const resolvedStorageDir = path26.resolve(storageDir);
17229
- const resolvedMemoryDir = path26.resolve(this.config.memoryDir);
17429
+ const resolvedStorageDir = path27.resolve(storageDir);
17430
+ const resolvedMemoryDir = path27.resolve(this.config.memoryDir);
17230
17431
  if (resolvedStorageDir === resolvedMemoryDir) return this.config.defaultNamespace;
17231
17432
  const m = resolvedStorageDir.match(/[\\/]namespaces[\\/]([^\\/]+)$/);
17232
17433
  return m && m[1] ? m[1] : this.config.defaultNamespace;
@@ -17254,14 +17455,14 @@ ${lines.join("\n\n")}`;
17254
17455
  };
17255
17456
 
17256
17457
  // src/tools.ts
17257
- import path28 from "path";
17458
+ import path29 from "path";
17258
17459
  import { createHash as createHash7 } from "crypto";
17259
17460
  import { Type } from "@sinclair/typebox";
17260
17461
 
17261
17462
  // src/work/storage.ts
17262
- import path27 from "path";
17463
+ import path28 from "path";
17263
17464
  import { randomUUID } from "crypto";
17264
- import { mkdir as mkdir20, readdir as readdir12, readFile as readFile19, rm as rm3, writeFile as writeFile18 } from "fs/promises";
17465
+ import { mkdir as mkdir21, readdir as readdir12, readFile as readFile20, rm as rm3, writeFile as writeFile19 } from "fs/promises";
17265
17466
  var TASK_TRANSITIONS = {
17266
17467
  todo: /* @__PURE__ */ new Set(["in_progress", "blocked", "cancelled"]),
17267
17468
  in_progress: /* @__PURE__ */ new Set(["todo", "blocked", "done", "cancelled"]),
@@ -17336,22 +17537,22 @@ function ensureProjectStatus(value) {
17336
17537
  var WorkStorage = class {
17337
17538
  constructor(memoryDir) {
17338
17539
  this.memoryDir = memoryDir;
17339
- this.tasksDir = path27.join(memoryDir, "work", "tasks");
17340
- this.projectsDir = path27.join(memoryDir, "work", "projects");
17540
+ this.tasksDir = path28.join(memoryDir, "work", "tasks");
17541
+ this.projectsDir = path28.join(memoryDir, "work", "projects");
17341
17542
  }
17342
17543
  tasksDir;
17343
17544
  projectsDir;
17344
17545
  async ensureDirectories() {
17345
- await mkdir20(this.tasksDir, { recursive: true });
17346
- await mkdir20(this.projectsDir, { recursive: true });
17546
+ await mkdir21(this.tasksDir, { recursive: true });
17547
+ await mkdir21(this.projectsDir, { recursive: true });
17347
17548
  }
17348
17549
  taskPath(id) {
17349
17550
  assertValidWorkId(id, "task");
17350
- return path27.join(this.tasksDir, `${id}.md`);
17551
+ return path28.join(this.tasksDir, `${id}.md`);
17351
17552
  }
17352
17553
  projectPath(id) {
17353
17554
  assertValidWorkId(id, "project");
17354
- return path27.join(this.projectsDir, `${id}.md`);
17555
+ return path28.join(this.projectsDir, `${id}.md`);
17355
17556
  }
17356
17557
  serializeTask(task) {
17357
17558
  return `${serializeFrontmatter2(task)}
@@ -17423,7 +17624,7 @@ ${project.description}
17423
17624
  throw new Error(`project not found: ${task.projectId}`);
17424
17625
  }
17425
17626
  }
17426
- await writeFile18(this.taskPath(task.id), this.serializeTask(task), "utf-8");
17627
+ await writeFile19(this.taskPath(task.id), this.serializeTask(task), "utf-8");
17427
17628
  if (task.projectId) {
17428
17629
  await this.addTaskIdToProject(task.projectId, task.id, now);
17429
17630
  }
@@ -17431,7 +17632,7 @@ ${project.description}
17431
17632
  }
17432
17633
  async getTask(id) {
17433
17634
  try {
17434
- const raw = await readFile19(this.taskPath(id), "utf-8");
17635
+ const raw = await readFile20(this.taskPath(id), "utf-8");
17435
17636
  return this.parseTask(raw);
17436
17637
  } catch {
17437
17638
  return null;
@@ -17443,7 +17644,7 @@ ${project.description}
17443
17644
  const out = [];
17444
17645
  for (const entry of entries) {
17445
17646
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
17446
- const raw = await readFile19(path27.join(this.tasksDir, entry.name), "utf-8");
17647
+ const raw = await readFile20(path28.join(this.tasksDir, entry.name), "utf-8");
17447
17648
  const task = this.parseTask(raw);
17448
17649
  if (!task) continue;
17449
17650
  if (filter?.status && task.status !== filter.status) continue;
@@ -17485,7 +17686,7 @@ ${project.description}
17485
17686
  tags: patch.tags ?? existing.tags,
17486
17687
  updatedAt: now.toISOString()
17487
17688
  };
17488
- await writeFile18(this.taskPath(id), this.serializeTask(next), "utf-8");
17689
+ await writeFile19(this.taskPath(id), this.serializeTask(next), "utf-8");
17489
17690
  return next;
17490
17691
  }
17491
17692
  async transitionTask(id, nextStatus, now = /* @__PURE__ */ new Date()) {
@@ -17525,12 +17726,12 @@ ${project.description}
17525
17726
  createdAt: timestamp,
17526
17727
  updatedAt: timestamp
17527
17728
  };
17528
- await writeFile18(this.projectPath(project.id), this.serializeProject(project), "utf-8");
17729
+ await writeFile19(this.projectPath(project.id), this.serializeProject(project), "utf-8");
17529
17730
  return project;
17530
17731
  }
17531
17732
  async getProject(id) {
17532
17733
  try {
17533
- const raw = await readFile19(this.projectPath(id), "utf-8");
17734
+ const raw = await readFile20(this.projectPath(id), "utf-8");
17534
17735
  return this.parseProject(raw);
17535
17736
  } catch {
17536
17737
  return null;
@@ -17542,7 +17743,7 @@ ${project.description}
17542
17743
  const out = [];
17543
17744
  for (const entry of entries) {
17544
17745
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
17545
- const raw = await readFile19(path27.join(this.projectsDir, entry.name), "utf-8");
17746
+ const raw = await readFile20(path28.join(this.projectsDir, entry.name), "utf-8");
17546
17747
  const project = this.parseProject(raw);
17547
17748
  if (project) out.push(project);
17548
17749
  }
@@ -17559,7 +17760,7 @@ ${project.description}
17559
17760
  taskIds: patch.taskIds ? [...patch.taskIds].sort() : existing.taskIds,
17560
17761
  updatedAt: now.toISOString()
17561
17762
  };
17562
- await writeFile18(this.projectPath(id), this.serializeProject(next), "utf-8");
17763
+ await writeFile19(this.projectPath(id), this.serializeProject(next), "utf-8");
17563
17764
  return next;
17564
17765
  }
17565
17766
  async deleteProject(id) {
@@ -19098,7 +19299,7 @@ Best for:
19098
19299
  - Reviewing identity development over time`,
19099
19300
  parameters: Type.Object({}),
19100
19301
  async execute() {
19101
- const workspaceDir = path28.join(process.env.HOME ?? "~", ".openclaw", "workspace");
19302
+ const workspaceDir = path29.join(process.env.HOME ?? "~", ".openclaw", "workspace");
19102
19303
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
19103
19304
  if (!identity) {
19104
19305
  return toolResult("No identity file found. Identity reflections build automatically through conversations when identityEnabled is true.");
@@ -19607,12 +19808,12 @@ mistakes: ${res.mistakesCount} patterns`
19607
19808
  }
19608
19809
 
19609
19810
  // src/cli.ts
19610
- import path44 from "path";
19611
- import { access as access3, readFile as readFile30, readdir as readdir19, unlink as unlink5 } from "fs/promises";
19811
+ import path45 from "path";
19812
+ import { access as access3, readFile as readFile31, readdir as readdir19, unlink as unlink5 } from "fs/promises";
19612
19813
 
19613
19814
  // src/transfer/export-json.ts
19614
- import path30 from "path";
19615
- import { mkdir as mkdir22, readFile as readFile21 } from "fs/promises";
19815
+ import path31 from "path";
19816
+ import { mkdir as mkdir23, readFile as readFile22 } from "fs/promises";
19616
19817
 
19617
19818
  // src/transfer/constants.ts
19618
19819
  var EXPORT_FORMAT = "openclaw-engram-export";
@@ -19620,10 +19821,10 @@ var EXPORT_SCHEMA_VERSION = 1;
19620
19821
 
19621
19822
  // src/transfer/fs-utils.ts
19622
19823
  import { createHash as createHash8 } from "crypto";
19623
- import { mkdir as mkdir21, readdir as readdir13, readFile as readFile20, stat as stat6, writeFile as writeFile19 } from "fs/promises";
19624
- import path29 from "path";
19824
+ import { mkdir as mkdir22, readdir as readdir13, readFile as readFile21, stat as stat6, writeFile as writeFile20 } from "fs/promises";
19825
+ import path30 from "path";
19625
19826
  async function sha256File(filePath) {
19626
- const buf = await readFile20(filePath);
19827
+ const buf = await readFile21(filePath);
19627
19828
  const sha256 = createHash8("sha256").update(buf).digest("hex");
19628
19829
  return { sha256, bytes: buf.byteLength };
19629
19830
  }
@@ -19633,11 +19834,11 @@ function sha256String(content) {
19633
19834
  return { sha256, bytes: buf.byteLength };
19634
19835
  }
19635
19836
  async function writeJsonFile(filePath, value) {
19636
- await mkdir21(path29.dirname(filePath), { recursive: true });
19637
- await writeFile19(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
19837
+ await mkdir22(path30.dirname(filePath), { recursive: true });
19838
+ await writeFile20(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
19638
19839
  }
19639
19840
  async function readJsonFile(filePath) {
19640
- const raw = await readFile20(filePath, "utf-8");
19841
+ const raw = await readFile21(filePath, "utf-8");
19641
19842
  return JSON.parse(raw);
19642
19843
  }
19643
19844
  async function listFilesRecursive(rootDir) {
@@ -19645,7 +19846,7 @@ async function listFilesRecursive(rootDir) {
19645
19846
  async function walk(dir) {
19646
19847
  const entries = await readdir13(dir, { withFileTypes: true });
19647
19848
  for (const ent of entries) {
19648
- const fp = path29.join(dir, ent.name);
19849
+ const fp = path30.join(dir, ent.name);
19649
19850
  if (ent.isDirectory()) {
19650
19851
  await walk(fp);
19651
19852
  } else if (ent.isFile()) {
@@ -19665,11 +19866,11 @@ async function fileExists(filePath) {
19665
19866
  }
19666
19867
  }
19667
19868
  function toPosixRelPath(absPath, rootDir) {
19668
- const rel = path29.relative(rootDir, absPath);
19669
- return rel.split(path29.sep).join("/");
19869
+ const rel = path30.relative(rootDir, absPath);
19870
+ return rel.split(path30.sep).join("/");
19670
19871
  }
19671
19872
  function fromPosixRelPath(relPath) {
19672
- return relPath.split("/").join(path29.sep);
19873
+ return relPath.split("/").join(path30.sep);
19673
19874
  }
19674
19875
 
19675
19876
  // src/transfer/export-json.ts
@@ -19685,24 +19886,24 @@ function shouldExclude(relPosix, includeTranscripts) {
19685
19886
  }
19686
19887
  async function exportJsonBundle(opts) {
19687
19888
  const includeTranscripts = opts.includeTranscripts === true;
19688
- const outDirAbs = path30.resolve(opts.outDir);
19689
- await mkdir22(outDirAbs, { recursive: true });
19690
- const memoryDirAbs = path30.resolve(opts.memoryDir);
19889
+ const outDirAbs = path31.resolve(opts.outDir);
19890
+ await mkdir23(outDirAbs, { recursive: true });
19891
+ const memoryDirAbs = path31.resolve(opts.memoryDir);
19691
19892
  const filesAbs = await listFilesRecursive(memoryDirAbs);
19692
19893
  const records = [];
19693
19894
  const manifestFiles = [];
19694
19895
  for (const abs of filesAbs) {
19695
19896
  const relPosix = toPosixRelPath(abs, memoryDirAbs);
19696
19897
  if (shouldExclude(relPosix, includeTranscripts)) continue;
19697
- const content = await readFile21(abs, "utf-8");
19898
+ const content = await readFile22(abs, "utf-8");
19698
19899
  records.push({ path: relPosix, content });
19699
19900
  const { sha256, bytes } = await sha256File(abs);
19700
19901
  manifestFiles.push({ path: relPosix, sha256, bytes });
19701
19902
  }
19702
19903
  if (opts.includeWorkspaceIdentity !== false && opts.workspaceDir) {
19703
- const identityPath = path30.join(opts.workspaceDir, "IDENTITY.md");
19904
+ const identityPath = path31.join(opts.workspaceDir, "IDENTITY.md");
19704
19905
  try {
19705
- const content = await readFile21(identityPath, "utf-8");
19906
+ const content = await readFile22(identityPath, "utf-8");
19706
19907
  const relPath = "workspace/IDENTITY.md";
19707
19908
  records.push({ path: relPath, content });
19708
19909
  const { sha256, bytes } = sha256String(content);
@@ -19719,13 +19920,13 @@ async function exportJsonBundle(opts) {
19719
19920
  files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
19720
19921
  };
19721
19922
  const bundle = { manifest, records };
19722
- await writeJsonFile(path30.join(outDirAbs, "manifest.json"), manifest);
19723
- await writeJsonFile(path30.join(outDirAbs, "bundle.json"), bundle);
19923
+ await writeJsonFile(path31.join(outDirAbs, "manifest.json"), manifest);
19924
+ await writeJsonFile(path31.join(outDirAbs, "bundle.json"), bundle);
19724
19925
  }
19725
19926
 
19726
19927
  // src/transfer/export-md.ts
19727
- import path31 from "path";
19728
- import { mkdir as mkdir23, readFile as readFile22, writeFile as writeFile20 } from "fs/promises";
19928
+ import path32 from "path";
19929
+ import { mkdir as mkdir24, readFile as readFile23, writeFile as writeFile21 } from "fs/promises";
19729
19930
  function shouldExclude2(relPosix, includeTranscripts) {
19730
19931
  const parts = relPosix.split("/");
19731
19932
  if (!includeTranscripts && parts[0] === "transcripts") return true;
@@ -19733,18 +19934,18 @@ function shouldExclude2(relPosix, includeTranscripts) {
19733
19934
  }
19734
19935
  async function exportMarkdownBundle(opts) {
19735
19936
  const includeTranscripts = opts.includeTranscripts === true;
19736
- const outDirAbs = path31.resolve(opts.outDir);
19737
- await mkdir23(outDirAbs, { recursive: true });
19738
- const memDirAbs = path31.resolve(opts.memoryDir);
19937
+ const outDirAbs = path32.resolve(opts.outDir);
19938
+ await mkdir24(outDirAbs, { recursive: true });
19939
+ const memDirAbs = path32.resolve(opts.memoryDir);
19739
19940
  const filesAbs = await listFilesRecursive(memDirAbs);
19740
19941
  const manifestFiles = [];
19741
19942
  for (const abs of filesAbs) {
19742
19943
  const relPosix = toPosixRelPath(abs, memDirAbs);
19743
19944
  if (shouldExclude2(relPosix, includeTranscripts)) continue;
19744
- const dstAbs = path31.join(outDirAbs, ...relPosix.split("/"));
19745
- await mkdir23(path31.dirname(dstAbs), { recursive: true });
19746
- const content = await readFile22(abs);
19747
- await writeFile20(dstAbs, content);
19945
+ const dstAbs = path32.join(outDirAbs, ...relPosix.split("/"));
19946
+ await mkdir24(path32.dirname(dstAbs), { recursive: true });
19947
+ const content = await readFile23(abs);
19948
+ await writeFile21(dstAbs, content);
19748
19949
  const { sha256, bytes } = await sha256File(abs);
19749
19950
  manifestFiles.push({ path: relPosix, sha256, bytes });
19750
19951
  }
@@ -19756,12 +19957,12 @@ async function exportMarkdownBundle(opts) {
19756
19957
  includesTranscripts: includeTranscripts,
19757
19958
  files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
19758
19959
  };
19759
- await writeJsonFile(path31.join(outDirAbs, "manifest.json"), manifest);
19960
+ await writeJsonFile(path32.join(outDirAbs, "manifest.json"), manifest);
19760
19961
  }
19761
19962
  async function looksLikeEngramMdExport(fromDir) {
19762
- const dirAbs = path31.resolve(fromDir);
19963
+ const dirAbs = path32.resolve(fromDir);
19763
19964
  try {
19764
- const raw = await readFile22(path31.join(dirAbs, "manifest.json"), "utf-8");
19965
+ const raw = await readFile23(path32.join(dirAbs, "manifest.json"), "utf-8");
19765
19966
  const parsed = JSON.parse(raw);
19766
19967
  return parsed.format === EXPORT_FORMAT && parsed.schemaVersion === EXPORT_SCHEMA_VERSION;
19767
19968
  } catch {
@@ -19770,16 +19971,16 @@ async function looksLikeEngramMdExport(fromDir) {
19770
19971
  }
19771
19972
 
19772
19973
  // src/transfer/backup.ts
19773
- import path32 from "path";
19774
- import { mkdir as mkdir24, readdir as readdir14, rm as rm4 } from "fs/promises";
19974
+ import path33 from "path";
19975
+ import { mkdir as mkdir25, readdir as readdir14, rm as rm4 } from "fs/promises";
19775
19976
  function timestampDirName(now) {
19776
19977
  return now.toISOString().replace(/[:.]/g, "-");
19777
19978
  }
19778
19979
  async function backupMemoryDir(opts) {
19779
- const outDirAbs = path32.resolve(opts.outDir);
19780
- await mkdir24(outDirAbs, { recursive: true });
19980
+ const outDirAbs = path33.resolve(opts.outDir);
19981
+ await mkdir25(outDirAbs, { recursive: true });
19781
19982
  const ts = timestampDirName(/* @__PURE__ */ new Date());
19782
- const backupDir = path32.join(outDirAbs, ts);
19983
+ const backupDir = path33.join(outDirAbs, ts);
19783
19984
  await exportMarkdownBundle({
19784
19985
  memoryDir: opts.memoryDir,
19785
19986
  outDir: backupDir,
@@ -19804,15 +20005,15 @@ async function enforceRetention(outDirAbs, retentionDays) {
19804
20005
  const tsMs = iso ? Date.parse(iso) : NaN;
19805
20006
  if (!Number.isFinite(tsMs)) continue;
19806
20007
  if (tsMs < cutoffMs) {
19807
- await rm4(path32.join(outDirAbs, name), { recursive: true, force: true });
20008
+ await rm4(path33.join(outDirAbs, name), { recursive: true, force: true });
19808
20009
  }
19809
20010
  }
19810
20011
  }
19811
20012
 
19812
20013
  // src/transfer/export-sqlite.ts
19813
- import path33 from "path";
20014
+ import path34 from "path";
19814
20015
  import Database from "better-sqlite3";
19815
- import { readFile as readFile23 } from "fs/promises";
20016
+ import { readFile as readFile24 } from "fs/promises";
19816
20017
 
19817
20018
  // src/transfer/sqlite-schema.ts
19818
20019
  var SQLITE_SCHEMA_VERSION = 1;
@@ -19838,8 +20039,8 @@ function shouldExclude3(relPosix, includeTranscripts) {
19838
20039
  }
19839
20040
  async function exportSqlite(opts) {
19840
20041
  const includeTranscripts = opts.includeTranscripts === true;
19841
- const memDirAbs = path33.resolve(opts.memoryDir);
19842
- const outAbs = path33.resolve(opts.outFile);
20042
+ const memDirAbs = path34.resolve(opts.memoryDir);
20043
+ const outAbs = path34.resolve(opts.outFile);
19843
20044
  const filesAbs = await listFilesRecursive(memDirAbs);
19844
20045
  const db = new Database(outAbs);
19845
20046
  try {
@@ -19860,7 +20061,7 @@ async function exportSqlite(opts) {
19860
20061
  for (const abs of filesAbs) {
19861
20062
  const relPosix = toPosixRelPath(abs, memDirAbs);
19862
20063
  if (shouldExclude3(relPosix, includeTranscripts)) continue;
19863
- const content = await readFile23(abs, "utf-8");
20064
+ const content = await readFile24(abs, "utf-8");
19864
20065
  const { sha256, bytes } = await sha256File(abs);
19865
20066
  rows.push({ rel: relPosix, bytes, sha256, content });
19866
20067
  }
@@ -19871,8 +20072,8 @@ async function exportSqlite(opts) {
19871
20072
  }
19872
20073
 
19873
20074
  // src/transfer/import-json.ts
19874
- import path34 from "path";
19875
- import { mkdir as mkdir25, writeFile as writeFile21 } from "fs/promises";
20075
+ import path35 from "path";
20076
+ import { mkdir as mkdir26, writeFile as writeFile22 } from "fs/promises";
19876
20077
 
19877
20078
  // src/transfer/types.ts
19878
20079
  import { z as z4 } from "zod";
@@ -19905,21 +20106,21 @@ function normalizeForDedupe(s) {
19905
20106
  }
19906
20107
  async function importJsonBundle(opts) {
19907
20108
  const conflict = opts.conflict ?? "skip";
19908
- const fromDirAbs = path34.resolve(opts.fromDir);
19909
- const bundlePath = path34.join(fromDirAbs, "bundle.json");
20109
+ const fromDirAbs = path35.resolve(opts.fromDir);
20110
+ const bundlePath = path35.join(fromDirAbs, "bundle.json");
19910
20111
  const bundle = ExportBundleV1Schema.parse(await readJsonFile(bundlePath));
19911
- const memDirAbs = path34.resolve(opts.targetMemoryDir);
20112
+ const memDirAbs = path35.resolve(opts.targetMemoryDir);
19912
20113
  const written = [];
19913
20114
  let skipped = 0;
19914
20115
  for (const rec of bundle.records) {
19915
20116
  const isWorkspace = rec.path.startsWith("workspace/");
19916
- const targetBase = isWorkspace ? opts.workspaceDir ? path34.resolve(opts.workspaceDir) : null : memDirAbs;
20117
+ const targetBase = isWorkspace ? opts.workspaceDir ? path35.resolve(opts.workspaceDir) : null : memDirAbs;
19917
20118
  if (isWorkspace && !targetBase) {
19918
20119
  skipped += 1;
19919
20120
  continue;
19920
20121
  }
19921
20122
  const relFs = fromPosixRelPath(isWorkspace ? rec.path.replace(/^workspace\//, "") : rec.path);
19922
- const absTarget = path34.join(targetBase, relFs);
20123
+ const absTarget = path35.join(targetBase, relFs);
19923
20124
  const exists3 = await fileExists(absTarget);
19924
20125
  if (exists3) {
19925
20126
  if (conflict === "skip") {
@@ -19943,30 +20144,30 @@ async function importJsonBundle(opts) {
19943
20144
  return { written: 0, skipped };
19944
20145
  }
19945
20146
  for (const w of written) {
19946
- await mkdir25(path34.dirname(w.abs), { recursive: true });
19947
- await writeFile21(w.abs, w.content, "utf-8");
20147
+ await mkdir26(path35.dirname(w.abs), { recursive: true });
20148
+ await writeFile22(w.abs, w.content, "utf-8");
19948
20149
  }
19949
20150
  return { written: written.length, skipped };
19950
20151
  }
19951
20152
  function looksLikeEngramJsonExport(fromDir) {
19952
- const dir = path34.resolve(fromDir);
20153
+ const dir = path35.resolve(fromDir);
19953
20154
  return Promise.all([
19954
- fileExists(path34.join(dir, "manifest.json")),
19955
- fileExists(path34.join(dir, "bundle.json"))
20155
+ fileExists(path35.join(dir, "manifest.json")),
20156
+ fileExists(path35.join(dir, "bundle.json"))
19956
20157
  ]).then(([m, b]) => m && b);
19957
20158
  }
19958
20159
 
19959
20160
  // src/transfer/import-sqlite.ts
19960
- import path35 from "path";
20161
+ import path36 from "path";
19961
20162
  import Database2 from "better-sqlite3";
19962
- import { mkdir as mkdir26, writeFile as writeFile22 } from "fs/promises";
20163
+ import { mkdir as mkdir27, writeFile as writeFile23 } from "fs/promises";
19963
20164
  function normalizeForDedupe2(s) {
19964
20165
  return s.replace(/\s+/g, " ").trim();
19965
20166
  }
19966
20167
  async function importSqlite(opts) {
19967
20168
  const conflict = opts.conflict ?? "skip";
19968
- const memDirAbs = path35.resolve(opts.targetMemoryDir);
19969
- const fromAbs = path35.resolve(opts.fromFile);
20169
+ const memDirAbs = path36.resolve(opts.targetMemoryDir);
20170
+ const fromAbs = path36.resolve(opts.fromFile);
19970
20171
  const db = new Database2(fromAbs, { readonly: true });
19971
20172
  const written = [];
19972
20173
  let skipped = 0;
@@ -19979,7 +20180,7 @@ async function importSqlite(opts) {
19979
20180
  const rows = db.prepare("SELECT path_rel, content FROM files").all();
19980
20181
  for (const r of rows) {
19981
20182
  const relFs = fromPosixRelPath(r.path_rel);
19982
- const absTarget = path35.join(memDirAbs, relFs);
20183
+ const absTarget = path36.join(memDirAbs, relFs);
19983
20184
  const exists3 = await fileExists(absTarget);
19984
20185
  if (exists3) {
19985
20186
  if (conflict === "skip") {
@@ -20004,30 +20205,30 @@ async function importSqlite(opts) {
20004
20205
  }
20005
20206
  if (opts.dryRun) return { written: 0, skipped };
20006
20207
  for (const w of written) {
20007
- await mkdir26(path35.dirname(w.abs), { recursive: true });
20008
- await writeFile22(w.abs, w.content, "utf-8");
20208
+ await mkdir27(path36.dirname(w.abs), { recursive: true });
20209
+ await writeFile23(w.abs, w.content, "utf-8");
20009
20210
  }
20010
20211
  return { written: written.length, skipped };
20011
20212
  }
20012
20213
 
20013
20214
  // src/transfer/import-md.ts
20014
- import path36 from "path";
20015
- import { mkdir as mkdir27, readFile as readFile24, writeFile as writeFile23 } from "fs/promises";
20215
+ import path37 from "path";
20216
+ import { mkdir as mkdir28, readFile as readFile25, writeFile as writeFile24 } from "fs/promises";
20016
20217
  function normalizeForDedupe3(s) {
20017
20218
  return s.replace(/\s+/g, " ").trim();
20018
20219
  }
20019
20220
  async function importMarkdownBundle(opts) {
20020
20221
  const conflict = opts.conflict ?? "skip";
20021
- const fromAbs = path36.resolve(opts.fromDir);
20022
- const targetAbs = path36.resolve(opts.targetMemoryDir);
20222
+ const fromAbs = path37.resolve(opts.fromDir);
20223
+ const targetAbs = path37.resolve(opts.targetMemoryDir);
20023
20224
  const filesAbs = await listFilesRecursive(fromAbs);
20024
20225
  const writes = [];
20025
20226
  let skipped = 0;
20026
20227
  for (const abs of filesAbs) {
20027
20228
  const relPosix = toPosixRelPath(abs, fromAbs);
20028
20229
  if (relPosix === "manifest.json") continue;
20029
- const dstAbs = path36.join(targetAbs, fromPosixRelPath(relPosix));
20030
- const content = await readFile24(abs, "utf-8");
20230
+ const dstAbs = path37.join(targetAbs, fromPosixRelPath(relPosix));
20231
+ const content = await readFile25(abs, "utf-8");
20031
20232
  const exists3 = await fileExists(dstAbs);
20032
20233
  if (exists3) {
20033
20234
  if (conflict === "skip") {
@@ -20049,17 +20250,17 @@ async function importMarkdownBundle(opts) {
20049
20250
  }
20050
20251
  if (opts.dryRun) return { written: 0, skipped };
20051
20252
  for (const w of writes) {
20052
- await mkdir27(path36.dirname(w.abs), { recursive: true });
20053
- await writeFile23(w.abs, w.content, "utf-8");
20253
+ await mkdir28(path37.dirname(w.abs), { recursive: true });
20254
+ await writeFile24(w.abs, w.content, "utf-8");
20054
20255
  }
20055
20256
  return { written: writes.length, skipped };
20056
20257
  }
20057
20258
 
20058
20259
  // src/transfer/autodetect.ts
20059
- import path37 from "path";
20260
+ import path38 from "path";
20060
20261
  import { stat as stat7 } from "fs/promises";
20061
20262
  async function detectImportFormat(fromPath) {
20062
- const abs = path37.resolve(fromPath);
20263
+ const abs = path38.resolve(fromPath);
20063
20264
  let st;
20064
20265
  try {
20065
20266
  st = await stat7(abs);
@@ -20487,8 +20688,8 @@ function gatherCandidates(input, warnings) {
20487
20688
  const record = rec;
20488
20689
  const content = typeof record.content === "string" ? record.content : null;
20489
20690
  if (!content) continue;
20490
- const path46 = typeof record.path === "string" ? record.path : "";
20491
- if (!path46.startsWith("transcripts/") && !path46.includes("/transcripts/")) continue;
20691
+ const path47 = typeof record.path === "string" ? record.path : "";
20692
+ if (!path47.startsWith("transcripts/") && !path47.includes("/transcripts/")) continue;
20492
20693
  rows.push(...parseJsonl(content, warnings));
20493
20694
  }
20494
20695
  return rows;
@@ -20544,8 +20745,8 @@ var openclawReplayNormalizer = {
20544
20745
  };
20545
20746
 
20546
20747
  // src/maintenance/archive-observations.ts
20547
- import path38 from "path";
20548
- import { mkdir as mkdir28, readdir as readdir15, readFile as readFile25, unlink as unlink4, writeFile as writeFile24 } from "fs/promises";
20748
+ import path39 from "path";
20749
+ import { mkdir as mkdir29, readdir as readdir15, readFile as readFile26, unlink as unlink4, writeFile as writeFile25 } from "fs/promises";
20549
20750
  var DATE_FILE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})\.(jsonl|md)$/;
20550
20751
  function normalizeRetentionDays(value) {
20551
20752
  if (!Number.isFinite(value)) return 30;
@@ -20571,8 +20772,8 @@ async function listFilesRecursive2(root, relPrefix = "") {
20571
20772
  return out;
20572
20773
  }
20573
20774
  for (const entry of entries) {
20574
- const rel = relPrefix ? path38.join(relPrefix, entry.name) : entry.name;
20575
- const full = path38.join(root, entry.name);
20775
+ const rel = relPrefix ? path39.join(relPrefix, entry.name) : entry.name;
20776
+ const full = path39.join(root, entry.name);
20576
20777
  if (entry.isDirectory()) {
20577
20778
  out.push(...await listFilesRecursive2(full, rel));
20578
20779
  continue;
@@ -20582,19 +20783,19 @@ async function listFilesRecursive2(root, relPrefix = "") {
20582
20783
  return out;
20583
20784
  }
20584
20785
  async function collectArchiveCandidates(memoryDir, cutoffTimeMs) {
20585
- const roots = ["transcripts", path38.join("state", "tool-usage"), path38.join("summaries", "hourly")];
20786
+ const roots = ["transcripts", path39.join("state", "tool-usage"), path39.join("summaries", "hourly")];
20586
20787
  const out = [];
20587
20788
  for (const relRoot of roots) {
20588
- const absRoot = path38.join(memoryDir, relRoot);
20789
+ const absRoot = path39.join(memoryDir, relRoot);
20589
20790
  const files = await listFilesRecursive2(absRoot);
20590
20791
  for (const fileRel of files) {
20591
- const filename = path38.basename(fileRel);
20792
+ const filename = path39.basename(fileRel);
20592
20793
  const parsedDate = extractDateFromFilename(filename);
20593
20794
  if (!parsedDate) continue;
20594
20795
  if (parsedDate.getTime() >= cutoffTimeMs) continue;
20595
20796
  out.push({
20596
- absolutePath: path38.join(absRoot, fileRel),
20597
- relativePath: path38.join(relRoot, fileRel)
20797
+ absolutePath: path39.join(absRoot, fileRel),
20798
+ relativePath: path39.join(relRoot, fileRel)
20598
20799
  });
20599
20800
  }
20600
20801
  }
@@ -20609,7 +20810,7 @@ async function archiveObservations(options) {
20609
20810
  new Date(now.getTime() - retentionDays * 24 * 60 * 60 * 1e3)
20610
20811
  );
20611
20812
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
20612
- const archiveRoot = path38.join(options.memoryDir, "archive", "observations", stamp);
20813
+ const archiveRoot = path39.join(options.memoryDir, "archive", "observations", stamp);
20613
20814
  const candidates = retentionDays === 0 ? [] : await collectArchiveCandidates(
20614
20815
  options.memoryDir,
20615
20816
  cutoffDayStartUtc
@@ -20618,13 +20819,13 @@ async function archiveObservations(options) {
20618
20819
  let archivedBytes = 0;
20619
20820
  const archivedRelativePaths = [];
20620
20821
  if (!dryRun && candidates.length > 0) {
20621
- await mkdir28(archiveRoot, { recursive: true });
20822
+ await mkdir29(archiveRoot, { recursive: true });
20622
20823
  for (const candidate of candidates) {
20623
- const archivePath = path38.join(archiveRoot, candidate.relativePath);
20624
- const archiveDir = path38.dirname(archivePath);
20625
- await mkdir28(archiveDir, { recursive: true });
20626
- const raw = await readFile25(candidate.absolutePath);
20627
- await writeFile24(archivePath, raw);
20824
+ const archivePath = path39.join(archiveRoot, candidate.relativePath);
20825
+ const archiveDir = path39.dirname(archivePath);
20826
+ await mkdir29(archiveDir, { recursive: true });
20827
+ const raw = await readFile26(candidate.absolutePath);
20828
+ await writeFile25(archivePath, raw);
20628
20829
  await unlink4(candidate.absolutePath);
20629
20830
  archivedFiles += 1;
20630
20831
  archivedBytes += raw.byteLength;
@@ -20645,12 +20846,12 @@ async function archiveObservations(options) {
20645
20846
  }
20646
20847
 
20647
20848
  // src/maintenance/rebuild-observations.ts
20648
- import path40 from "path";
20649
- import { readdir as readdir16, readFile as readFile27 } from "fs/promises";
20849
+ import path41 from "path";
20850
+ import { readdir as readdir16, readFile as readFile28 } from "fs/promises";
20650
20851
 
20651
20852
  // src/maintenance/observation-ledger-utils.ts
20652
- import path39 from "path";
20653
- import { mkdir as mkdir29, readFile as readFile26, writeFile as writeFile25 } from "fs/promises";
20853
+ import path40 from "path";
20854
+ import { mkdir as mkdir30, readFile as readFile27, writeFile as writeFile26 } from "fs/promises";
20654
20855
  function toHourBucketIso(timestamp) {
20655
20856
  const normalized = /(?:Z|[+-]\d{2}:\d{2})$/u.test(timestamp) ? timestamp : `${timestamp}Z`;
20656
20857
  const ms = Date.parse(normalized);
@@ -20661,17 +20862,17 @@ function toHourBucketIso(timestamp) {
20661
20862
  }
20662
20863
  async function backupAndWriteRebuiltObservations(options) {
20663
20864
  const stamp = options.now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
20664
- const archiveRoot = path39.join(options.memoryDir, "archive", "observations", stamp);
20665
- let backupPath = path39.join(
20865
+ const archiveRoot = path40.join(options.memoryDir, "archive", "observations", stamp);
20866
+ let backupPath = path40.join(
20666
20867
  archiveRoot,
20667
20868
  "state",
20668
20869
  "observation-ledger",
20669
20870
  "rebuilt-observations.jsonl"
20670
20871
  );
20671
20872
  try {
20672
- const existing = await readFile26(options.outputPath, "utf-8");
20673
- await mkdir29(path39.dirname(backupPath), { recursive: true });
20674
- await writeFile25(backupPath, existing, "utf-8");
20873
+ const existing = await readFile27(options.outputPath, "utf-8");
20874
+ await mkdir30(path40.dirname(backupPath), { recursive: true });
20875
+ await writeFile26(backupPath, existing, "utf-8");
20675
20876
  } catch (err) {
20676
20877
  const code = err.code;
20677
20878
  if (code && code === "ENOENT") {
@@ -20687,8 +20888,8 @@ async function backupAndWriteRebuiltObservations(options) {
20687
20888
  rebuiltAt
20688
20889
  })
20689
20890
  );
20690
- await mkdir29(path39.dirname(options.outputPath), { recursive: true });
20691
- await writeFile25(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
20891
+ await mkdir30(path40.dirname(options.outputPath), { recursive: true });
20892
+ await writeFile26(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
20692
20893
  ` : "", "utf-8");
20693
20894
  return backupPath;
20694
20895
  }
@@ -20710,7 +20911,7 @@ async function listTranscriptFiles(root) {
20710
20911
  for (const entry of entries) {
20711
20912
  if (entry.name === "." || entry.name === "..") continue;
20712
20913
  if (entry.isSymbolicLink()) continue;
20713
- const full = path40.join(root, entry.name);
20914
+ const full = path41.join(root, entry.name);
20714
20915
  if (entry.isDirectory()) {
20715
20916
  out.push(...await listTranscriptFiles(full));
20716
20917
  continue;
@@ -20770,8 +20971,8 @@ function buildLedgerRows(linesByFile) {
20770
20971
  async function rebuildObservations(options) {
20771
20972
  const dryRun = options.dryRun !== false;
20772
20973
  const now = options.now ?? /* @__PURE__ */ new Date();
20773
- const transcriptsRoot = path40.join(options.memoryDir, "transcripts");
20774
- const outputPath = path40.join(
20974
+ const transcriptsRoot = path41.join(options.memoryDir, "transcripts");
20975
+ const outputPath = path41.join(
20775
20976
  options.memoryDir,
20776
20977
  "state",
20777
20978
  "observation-ledger",
@@ -20781,7 +20982,7 @@ async function rebuildObservations(options) {
20781
20982
  const contents = [];
20782
20983
  for (const file of transcriptFiles) {
20783
20984
  try {
20784
- contents.push(await readFile27(file, "utf-8"));
20985
+ contents.push(await readFile28(file, "utf-8"));
20785
20986
  } catch {
20786
20987
  }
20787
20988
  }
@@ -20807,8 +21008,8 @@ async function rebuildObservations(options) {
20807
21008
  }
20808
21009
 
20809
21010
  // src/maintenance/migrate-observations.ts
20810
- import path41 from "path";
20811
- import { readdir as readdir17, readFile as readFile28 } from "fs/promises";
21011
+ import path42 from "path";
21012
+ import { readdir as readdir17, readFile as readFile29 } from "fs/promises";
20812
21013
  function toNonNegativeInt(value) {
20813
21014
  if (typeof value !== "number" || !Number.isFinite(value)) return null;
20814
21015
  const normalized = Math.floor(value);
@@ -20871,16 +21072,16 @@ async function listLegacyObservationFiles(root) {
20871
21072
  async function migrateObservations(options) {
20872
21073
  const dryRun = options.dryRun !== false;
20873
21074
  const now = options.now ?? /* @__PURE__ */ new Date();
20874
- const ledgerRoot = path41.join(options.memoryDir, "state", "observation-ledger");
20875
- const outputPath = path41.join(ledgerRoot, "rebuilt-observations.jsonl");
21075
+ const ledgerRoot = path42.join(options.memoryDir, "state", "observation-ledger");
21076
+ const outputPath = path42.join(ledgerRoot, "rebuilt-observations.jsonl");
20876
21077
  const legacyFiles = await listLegacyObservationFiles(ledgerRoot);
20877
- const sourceRelativePaths = legacyFiles.map((name) => path41.join("state", "observation-ledger", name));
21078
+ const sourceRelativePaths = legacyFiles.map((name) => path42.join("state", "observation-ledger", name));
20878
21079
  const byKey = /* @__PURE__ */ new Map();
20879
21080
  let parsedRows = 0;
20880
21081
  let malformedLines = 0;
20881
21082
  for (const file of legacyFiles) {
20882
- const full = path41.join(ledgerRoot, file);
20883
- const raw = await readFile28(full, "utf-8");
21083
+ const full = path42.join(ledgerRoot, file);
21084
+ const raw = await readFile29(full, "utf-8");
20884
21085
  for (const line of raw.split("\n")) {
20885
21086
  if (!line.trim()) continue;
20886
21087
  let parsed;
@@ -21069,10 +21270,10 @@ var defaultCommandRunner = (command, args, options) => {
21069
21270
 
21070
21271
  // src/network/webdav.ts
21071
21272
  import { createReadStream } from "fs";
21072
- import { mkdir as mkdir30, readdir as readdir18, realpath as realpath2, stat as stat9 } from "fs/promises";
21273
+ import { mkdir as mkdir31, readdir as readdir18, realpath as realpath2, stat as stat9 } from "fs/promises";
21073
21274
  import { createServer } from "http";
21074
21275
  import { timingSafeEqual } from "crypto";
21075
- import path42 from "path";
21276
+ import path43 from "path";
21076
21277
  import { pipeline } from "stream/promises";
21077
21278
  import { URL as URL2 } from "url";
21078
21279
  function hostToUrlAuthority(host) {
@@ -21108,10 +21309,10 @@ var WebDavServer = class _WebDavServer {
21108
21309
  const allowedRoots = [];
21109
21310
  const aliasSet = /* @__PURE__ */ new Set();
21110
21311
  for (const dir of options.allowlistDirs) {
21111
- const resolved = path42.resolve(dir);
21112
- await mkdir30(resolved, { recursive: true });
21312
+ const resolved = path43.resolve(dir);
21313
+ await mkdir31(resolved, { recursive: true });
21113
21314
  const canonical = await realpath2(resolved);
21114
- const alias = path42.basename(canonical) || "root";
21315
+ const alias = path43.basename(canonical) || "root";
21115
21316
  if (aliasSet.has(alias)) {
21116
21317
  throw new Error(`duplicate webdav allowlist alias: ${alias}`);
21117
21318
  }
@@ -21267,7 +21468,7 @@ var WebDavServer = class _WebDavServer {
21267
21468
  if (decodedPath.includes("\0")) {
21268
21469
  return { ok: false, code: 400, message: "invalid path" };
21269
21470
  }
21270
- const normalized = path42.posix.normalize(decodedPath);
21471
+ const normalized = path43.posix.normalize(decodedPath);
21271
21472
  const segments = normalized.split("/").filter((segment) => segment.length > 0);
21272
21473
  if (segments.length === 0) {
21273
21474
  return { ok: false, code: 403, message: "root listing is not allowed" };
@@ -21281,7 +21482,7 @@ var WebDavServer = class _WebDavServer {
21281
21482
  if (relative.some((segment) => segment === ".." || segment.includes("\\"))) {
21282
21483
  return { ok: false, code: 403, message: "path traversal is not allowed" };
21283
21484
  }
21284
- const candidate = path42.resolve(root.absolute, ...relative);
21485
+ const candidate = path43.resolve(root.absolute, ...relative);
21285
21486
  if (!this.isPathInside(root.absolute, candidate)) {
21286
21487
  return { ok: false, code: 403, message: "path escaped allowlist" };
21287
21488
  }
@@ -21359,10 +21560,10 @@ var WebDavServer = class _WebDavServer {
21359
21560
  }
21360
21561
  isPathInside(root, target) {
21361
21562
  if (target === root) return true;
21362
- if (root === path42.parse(root).root) {
21563
+ if (root === path43.parse(root).root) {
21363
21564
  return target.startsWith(root);
21364
21565
  }
21365
- return target.startsWith(`${root}${path42.sep}`);
21566
+ return target.startsWith(`${root}${path43.sep}`);
21366
21567
  }
21367
21568
  };
21368
21569
  function xmlEscape(value) {
@@ -21373,8 +21574,8 @@ function toEncodedHref(pathname) {
21373
21574
  }
21374
21575
 
21375
21576
  // src/compat/checks.ts
21376
- import { access as access2, readFile as readFile29 } from "fs/promises";
21377
- import path43 from "path";
21577
+ import { access as access2, readFile as readFile30 } from "fs/promises";
21578
+ import path44 from "path";
21378
21579
  import { spawn as spawn4 } from "child_process";
21379
21580
  var REQUIRED_HOOKS = ["before_agent_start", "agent_end"];
21380
21581
  function isSafeCommandToken(command) {
@@ -21556,13 +21757,13 @@ function compareVersions(a, b) {
21556
21757
  async function runCompatChecks(options) {
21557
21758
  const checks = [];
21558
21759
  const runner = options.runner ?? defaultRunner;
21559
- const pluginJsonPath = path43.join(options.repoRoot, "openclaw.plugin.json");
21560
- const packageJsonPath = path43.join(options.repoRoot, "package.json");
21561
- const indexPath = path43.join(options.repoRoot, "src", "index.ts");
21760
+ const pluginJsonPath = path44.join(options.repoRoot, "openclaw.plugin.json");
21761
+ const packageJsonPath = path44.join(options.repoRoot, "package.json");
21762
+ const indexPath = path44.join(options.repoRoot, "src", "index.ts");
21562
21763
  let pluginRaw = "";
21563
21764
  let pluginManifestPresent = false;
21564
21765
  try {
21565
- pluginRaw = await readFile29(pluginJsonPath, "utf-8");
21766
+ pluginRaw = await readFile30(pluginJsonPath, "utf-8");
21566
21767
  pluginManifestPresent = true;
21567
21768
  checks.push({
21568
21769
  id: "plugin-manifest-present",
@@ -21611,7 +21812,7 @@ async function runCompatChecks(options) {
21611
21812
  let packageRaw = "";
21612
21813
  let packageJsonPresent = false;
21613
21814
  try {
21614
- packageRaw = await readFile29(packageJsonPath, "utf-8");
21815
+ packageRaw = await readFile30(packageJsonPath, "utf-8");
21615
21816
  packageJsonPresent = true;
21616
21817
  } catch {
21617
21818
  checks.push({
@@ -21682,7 +21883,7 @@ async function runCompatChecks(options) {
21682
21883
  }
21683
21884
  try {
21684
21885
  await access2(indexPath);
21685
- const indexRaw = await readFile29(indexPath, "utf-8");
21886
+ const indexRaw = await readFile30(indexPath, "utf-8");
21686
21887
  const structuralSource = stripCommentsAndStrings(indexRaw);
21687
21888
  const hooks = parseHookRegistrations(indexRaw);
21688
21889
  const missingHooks = REQUIRED_HOOKS.filter((hook) => !hooks.has(hook));
@@ -22214,7 +22415,7 @@ async function withTimeout(promise, timeoutMs, timeoutMessage) {
22214
22415
  }
22215
22416
  async function runReplayCliCommand(orchestrator, options) {
22216
22417
  const extractionIdleTimeoutMs = Number.isFinite(options.extractionIdleTimeoutMs) ? Math.max(1e3, Math.floor(options.extractionIdleTimeoutMs)) : 15 * 6e4;
22217
- const inputRaw = await readFile30(options.inputPath, "utf-8");
22418
+ const inputRaw = await readFile31(options.inputPath, "utf-8");
22218
22419
  const registry = buildReplayNormalizerRegistry([
22219
22420
  openclawReplayNormalizer,
22220
22421
  claudeReplayNormalizer,
@@ -22279,7 +22480,7 @@ async function runReplayCliCommand(orchestrator, options) {
22279
22480
  async function getPluginVersion() {
22280
22481
  try {
22281
22482
  const pkgPath = new URL("../package.json", import.meta.url);
22282
- const raw = await readFile30(pkgPath, "utf-8");
22483
+ const raw = await readFile31(pkgPath, "utf-8");
22283
22484
  const parsed = JSON.parse(raw);
22284
22485
  return parsed.version ?? "unknown";
22285
22486
  } catch {
@@ -22298,14 +22499,14 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
22298
22499
  const ns = (namespace ?? "").trim();
22299
22500
  if (!ns) return orchestrator.config.memoryDir;
22300
22501
  if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
22301
- const candidate = path44.join(orchestrator.config.memoryDir, "namespaces", ns);
22502
+ const candidate = path45.join(orchestrator.config.memoryDir, "namespaces", ns);
22302
22503
  if (ns === orchestrator.config.defaultNamespace) {
22303
22504
  return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
22304
22505
  }
22305
22506
  return candidate;
22306
22507
  }
22307
22508
  async function readAllMemoryFiles(memoryDir) {
22308
- const roots = [path44.join(memoryDir, "facts"), path44.join(memoryDir, "corrections")];
22509
+ const roots = [path45.join(memoryDir, "facts"), path45.join(memoryDir, "corrections")];
22309
22510
  const out = [];
22310
22511
  const walk = async (dir) => {
22311
22512
  let entries;
@@ -22316,14 +22517,14 @@ async function readAllMemoryFiles(memoryDir) {
22316
22517
  }
22317
22518
  for (const entry of entries) {
22318
22519
  const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
22319
- const fullPath = path44.join(dir, entryName);
22520
+ const fullPath = path45.join(dir, entryName);
22320
22521
  if (entry.isDirectory()) {
22321
22522
  await walk(fullPath);
22322
22523
  continue;
22323
22524
  }
22324
22525
  if (!entry.isFile() || !entryName.endsWith(".md")) continue;
22325
22526
  try {
22326
- const raw = await readFile30(fullPath, "utf-8");
22527
+ const raw = await readFile31(fullPath, "utf-8");
22327
22528
  const parsed = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
22328
22529
  if (!parsed) continue;
22329
22530
  const fmRaw = parsed[1];
@@ -23131,7 +23332,7 @@ function registerCli(api, orchestrator) {
23131
23332
  }
23132
23333
  });
23133
23334
  cmd.command("identity").description("Show agent identity reflections").action(async () => {
23134
- const workspaceDir = path44.join(process.env.HOME ?? "~", ".openclaw", "workspace");
23335
+ const workspaceDir = path45.join(process.env.HOME ?? "~", ".openclaw", "workspace");
23135
23336
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
23136
23337
  if (!identity) {
23137
23338
  console.log("No identity file found.");
@@ -23354,8 +23555,8 @@ function registerCli(api, orchestrator) {
23354
23555
  const options = args[0] ?? {};
23355
23556
  const threadId = options.thread;
23356
23557
  const top = parseInt(options.top ?? "10", 10);
23357
- const memoryDir = path44.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
23358
- const threading = new ThreadingManager(path44.join(memoryDir, "threads"));
23558
+ const memoryDir = path45.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
23559
+ const threading = new ThreadingManager(path45.join(memoryDir, "threads"));
23359
23560
  if (threadId) {
23360
23561
  const thread = await threading.loadThread(threadId);
23361
23562
  if (!thread) {
@@ -23528,16 +23729,16 @@ function parseDuration(duration) {
23528
23729
  }
23529
23730
 
23530
23731
  // src/index.ts
23531
- import { readFile as readFile31, writeFile as writeFile26 } from "fs/promises";
23732
+ import { readFile as readFile32, writeFile as writeFile27 } from "fs/promises";
23532
23733
  import { readFileSync as readFileSync4 } from "fs";
23533
- import path45 from "path";
23734
+ import path46 from "path";
23534
23735
  import os5 from "os";
23535
23736
  var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
23536
23737
  function loadPluginConfigFromFile() {
23537
23738
  try {
23538
23739
  const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
23539
23740
  const homeDir = process.env.HOME ?? os5.homedir();
23540
- const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path45.join(homeDir, ".openclaw", "openclaw.json");
23741
+ const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path46.join(homeDir, ".openclaw", "openclaw.json");
23541
23742
  const content = readFileSync4(configPath, "utf-8");
23542
23743
  const config = JSON.parse(content);
23543
23744
  const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
@@ -23745,11 +23946,11 @@ Use this context naturally when relevant. Never quote or expose this memory cont
23745
23946
  );
23746
23947
  async function ensureHourlySummaryCron(api2) {
23747
23948
  const jobId = "engram-hourly-summary";
23748
- const cronFilePath = path45.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
23949
+ const cronFilePath = path46.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
23749
23950
  try {
23750
23951
  let jobsData = { version: 1, jobs: [] };
23751
23952
  try {
23752
- const content = await readFile31(cronFilePath, "utf-8");
23953
+ const content = await readFile32(cronFilePath, "utf-8");
23753
23954
  jobsData = JSON.parse(content);
23754
23955
  } catch {
23755
23956
  }
@@ -23786,7 +23987,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
23786
23987
  state: {}
23787
23988
  };
23788
23989
  jobsData.jobs.push(newJob);
23789
- await writeFile26(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
23990
+ await writeFile27(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
23790
23991
  log.info("auto-registered hourly summary cron job");
23791
23992
  } catch (err) {
23792
23993
  log.error("failed to auto-register hourly summary cron job:", err);