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