@joshuaswarren/openclaw-engram 8.3.6 → 8.3.8
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 +336 -5
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +70 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -312,6 +312,16 @@ function parseConfig(raw) {
|
|
|
312
312
|
factArchivalMaxImportance: typeof cfg.factArchivalMaxImportance === "number" ? cfg.factArchivalMaxImportance : 0.3,
|
|
313
313
|
factArchivalMaxAccessCount: typeof cfg.factArchivalMaxAccessCount === "number" ? cfg.factArchivalMaxAccessCount : 2,
|
|
314
314
|
factArchivalProtectedCategories: Array.isArray(cfg.factArchivalProtectedCategories) ? cfg.factArchivalProtectedCategories.filter((c) => typeof c === "string") : ["commitment", "preference", "decision", "principle"],
|
|
315
|
+
// v8.3 lifecycle policy engine (default off)
|
|
316
|
+
lifecyclePolicyEnabled: cfg.lifecyclePolicyEnabled === true,
|
|
317
|
+
lifecycleFilterStaleEnabled: cfg.lifecycleFilterStaleEnabled === true,
|
|
318
|
+
lifecyclePromoteHeatThreshold: typeof cfg.lifecyclePromoteHeatThreshold === "number" ? Math.min(1, Math.max(0, cfg.lifecyclePromoteHeatThreshold)) : 0.55,
|
|
319
|
+
lifecycleStaleDecayThreshold: typeof cfg.lifecycleStaleDecayThreshold === "number" ? Math.min(1, Math.max(0, cfg.lifecycleStaleDecayThreshold)) : 0.65,
|
|
320
|
+
lifecycleArchiveDecayThreshold: typeof cfg.lifecycleArchiveDecayThreshold === "number" ? Math.min(1, Math.max(0, cfg.lifecycleArchiveDecayThreshold)) : 0.85,
|
|
321
|
+
lifecycleProtectedCategories: Array.isArray(cfg.lifecycleProtectedCategories) ? cfg.lifecycleProtectedCategories.filter(
|
|
322
|
+
(c) => typeof c === "string" && VALID_MEMORY_CATEGORIES.has(c)
|
|
323
|
+
) : ["decision", "principle", "commitment", "preference"],
|
|
324
|
+
lifecycleMetricsEnabled: typeof cfg.lifecycleMetricsEnabled === "boolean" ? cfg.lifecycleMetricsEnabled : cfg.lifecyclePolicyEnabled === true,
|
|
315
325
|
// v8.0 phase 1
|
|
316
326
|
recallPlannerEnabled: cfg.recallPlannerEnabled !== false,
|
|
317
327
|
recallPlannerMaxQmdResultsMinimal: typeof cfg.recallPlannerMaxQmdResultsMinimal === "number" ? cfg.recallPlannerMaxQmdResultsMinimal : 4,
|
|
@@ -4989,6 +4999,37 @@ ${sanitized.text}
|
|
|
4989
4999
|
log.debug(`updated memory ${id}`);
|
|
4990
5000
|
return true;
|
|
4991
5001
|
}
|
|
5002
|
+
/**
|
|
5003
|
+
* Update frontmatter fields without changing memory content.
|
|
5004
|
+
* Returns false when the memory is not found.
|
|
5005
|
+
*/
|
|
5006
|
+
async writeMemoryFrontmatter(memory, patch) {
|
|
5007
|
+
const beforeStatus = memory.frontmatter.status ?? "active";
|
|
5008
|
+
const updated = {
|
|
5009
|
+
...memory.frontmatter,
|
|
5010
|
+
...patch
|
|
5011
|
+
};
|
|
5012
|
+
const afterStatus = updated.status ?? "active";
|
|
5013
|
+
const fileContent = `${serializeFrontmatter(updated)}
|
|
5014
|
+
|
|
5015
|
+
${memory.content}
|
|
5016
|
+
`;
|
|
5017
|
+
await writeFile2(memory.path, fileContent, "utf-8");
|
|
5018
|
+
if (beforeStatus !== afterStatus) {
|
|
5019
|
+
this.bumpMemoryStatusVersion();
|
|
5020
|
+
}
|
|
5021
|
+
return true;
|
|
5022
|
+
}
|
|
5023
|
+
/**
|
|
5024
|
+
* Update frontmatter by memory ID.
|
|
5025
|
+
* Prefer writeMemoryFrontmatter(memory, patch) in batch loops to avoid full-corpus rescans.
|
|
5026
|
+
*/
|
|
5027
|
+
async updateMemoryFrontmatter(id, patch) {
|
|
5028
|
+
const memories = await this.readAllMemories();
|
|
5029
|
+
const memory = memories.find((m) => m.frontmatter.id === id);
|
|
5030
|
+
if (!memory) return false;
|
|
5031
|
+
return this.writeMemoryFrontmatter(memory, patch);
|
|
5032
|
+
}
|
|
4992
5033
|
/** Remove memories past their TTL expiresAt date */
|
|
4993
5034
|
async cleanExpiredTTL() {
|
|
4994
5035
|
const memories = await this.readAllMemories();
|
|
@@ -5325,8 +5366,8 @@ ${reflection}
|
|
|
5325
5366
|
*/
|
|
5326
5367
|
static scoreEntity(entity, now) {
|
|
5327
5368
|
const updated = entity.updated ? new Date(entity.updated).getTime() : 0;
|
|
5328
|
-
const
|
|
5329
|
-
const recency = 1 / (1 +
|
|
5369
|
+
const daysSince2 = Math.max(0, (now.getTime() - updated) / (1e3 * 60 * 60 * 24));
|
|
5370
|
+
const recency = 1 / (1 + daysSince2 / 7);
|
|
5330
5371
|
const frequency = Math.min(entity.facts.length / 20, 1);
|
|
5331
5372
|
const activityScore = Math.min(entity.activity.length / 10, 1);
|
|
5332
5373
|
const TYPE_PRIORITY = {
|
|
@@ -8752,6 +8793,167 @@ var TmtBuilder = class {
|
|
|
8752
8793
|
}
|
|
8753
8794
|
};
|
|
8754
8795
|
|
|
8796
|
+
// src/lifecycle.ts
|
|
8797
|
+
var DEFAULT_POLICY = {
|
|
8798
|
+
promoteHeatThreshold: 0.55,
|
|
8799
|
+
staleDecayThreshold: 0.65,
|
|
8800
|
+
archiveDecayThreshold: 0.85,
|
|
8801
|
+
protectedCategories: ["decision", "principle", "commitment", "preference"]
|
|
8802
|
+
};
|
|
8803
|
+
function clamp01(value) {
|
|
8804
|
+
if (!Number.isFinite(value)) return 0;
|
|
8805
|
+
if (value < 0) return 0;
|
|
8806
|
+
if (value > 1) return 1;
|
|
8807
|
+
return value;
|
|
8808
|
+
}
|
|
8809
|
+
function parseIsoMs(value) {
|
|
8810
|
+
if (!value) return null;
|
|
8811
|
+
const ms = Date.parse(value);
|
|
8812
|
+
return Number.isFinite(ms) ? ms : null;
|
|
8813
|
+
}
|
|
8814
|
+
function daysSince(value, nowMs) {
|
|
8815
|
+
const ts = parseIsoMs(value);
|
|
8816
|
+
if (ts === null) return 365;
|
|
8817
|
+
return Math.max(0, (nowMs - ts) / 864e5);
|
|
8818
|
+
}
|
|
8819
|
+
function confidenceTierWeight(frontmatter) {
|
|
8820
|
+
switch (frontmatter.confidenceTier) {
|
|
8821
|
+
case "explicit":
|
|
8822
|
+
return 1;
|
|
8823
|
+
case "implied":
|
|
8824
|
+
return 0.8;
|
|
8825
|
+
case "inferred":
|
|
8826
|
+
return 0.6;
|
|
8827
|
+
case "speculative":
|
|
8828
|
+
return 0.35;
|
|
8829
|
+
default:
|
|
8830
|
+
return clamp01(frontmatter.confidence ?? 0.5);
|
|
8831
|
+
}
|
|
8832
|
+
}
|
|
8833
|
+
function accessWeight(accessCount) {
|
|
8834
|
+
const raw = accessCount ?? 0;
|
|
8835
|
+
if (raw <= 0) return 0;
|
|
8836
|
+
return clamp01(Math.log1p(raw) / Math.log1p(20));
|
|
8837
|
+
}
|
|
8838
|
+
function recencyWeight(frontmatter, nowMs) {
|
|
8839
|
+
const lastTouch = frontmatter.lastAccessed ?? frontmatter.updated ?? frontmatter.created;
|
|
8840
|
+
const ageDays = daysSince(lastTouch, nowMs);
|
|
8841
|
+
return clamp01(1 - ageDays / 90);
|
|
8842
|
+
}
|
|
8843
|
+
function feedbackWeight(signals) {
|
|
8844
|
+
const raw = signals?.feedbackScore ?? 0;
|
|
8845
|
+
return clamp01((raw + 1) / 2);
|
|
8846
|
+
}
|
|
8847
|
+
function isProtectedMemory(frontmatter, policy) {
|
|
8848
|
+
return frontmatter.policyClass === "protected" || policy.protectedCategories.includes(frontmatter.category);
|
|
8849
|
+
}
|
|
8850
|
+
function resolveLifecycleState(frontmatter) {
|
|
8851
|
+
if (frontmatter.status === "archived") return "archived";
|
|
8852
|
+
return frontmatter.lifecycleState ?? "candidate";
|
|
8853
|
+
}
|
|
8854
|
+
function computeHeat(memory, now, signals) {
|
|
8855
|
+
const frontmatter = memory.frontmatter;
|
|
8856
|
+
if (frontmatter.status === "archived") return 0;
|
|
8857
|
+
const nowMs = now.getTime();
|
|
8858
|
+
const confidence = confidenceTierWeight(frontmatter);
|
|
8859
|
+
const access3 = accessWeight(frontmatter.accessCount);
|
|
8860
|
+
const recency = recencyWeight(frontmatter, nowMs);
|
|
8861
|
+
const importance = clamp01(frontmatter.importance?.score ?? 0.5);
|
|
8862
|
+
const feedback = feedbackWeight(signals);
|
|
8863
|
+
const disputedPenalty = frontmatter.verificationState === "disputed" ? 0.2 : 0;
|
|
8864
|
+
const score = confidence * 0.25 + access3 * 0.3 + recency * 0.2 + importance * 0.15 + feedback * 0.1 - disputedPenalty;
|
|
8865
|
+
return clamp01(score);
|
|
8866
|
+
}
|
|
8867
|
+
function computeDecay(memory, now, signals) {
|
|
8868
|
+
const frontmatter = memory.frontmatter;
|
|
8869
|
+
if (frontmatter.status === "archived") return 1;
|
|
8870
|
+
const nowMs = now.getTime();
|
|
8871
|
+
const ageDays = daysSince(frontmatter.updated ?? frontmatter.created, nowMs);
|
|
8872
|
+
const staleAccessDays = daysSince(frontmatter.lastAccessed, nowMs);
|
|
8873
|
+
const ageRisk = clamp01(ageDays / 180);
|
|
8874
|
+
const staleAccessRisk = clamp01(staleAccessDays / 120);
|
|
8875
|
+
const confidenceRisk = 1 - confidenceTierWeight(frontmatter);
|
|
8876
|
+
const feedbackRisk = clamp01(((signals?.feedbackScore ?? 0) * -1 + 1) / 2);
|
|
8877
|
+
const heat = computeHeat(memory, now, signals);
|
|
8878
|
+
const score = ageRisk * 0.3 + staleAccessRisk * 0.25 + confidenceRisk * 0.2 + feedbackRisk * 0.1 + (1 - heat) * 0.15;
|
|
8879
|
+
return clamp01(score);
|
|
8880
|
+
}
|
|
8881
|
+
function toTerminalDisputedState(currentState) {
|
|
8882
|
+
if (currentState === "archived") return "archived";
|
|
8883
|
+
return "stale";
|
|
8884
|
+
}
|
|
8885
|
+
function isActiveEligible(verificationState) {
|
|
8886
|
+
return verificationState === "user_confirmed" || verificationState === "system_inferred";
|
|
8887
|
+
}
|
|
8888
|
+
function decideLifecycleTransition(memory, policy, now, signals) {
|
|
8889
|
+
const mergedPolicy = { ...DEFAULT_POLICY, ...policy };
|
|
8890
|
+
const frontmatter = memory.frontmatter;
|
|
8891
|
+
const currentState = resolveLifecycleState(frontmatter);
|
|
8892
|
+
const heatScore = computeHeat(memory, now, signals);
|
|
8893
|
+
const decayScore = computeDecay(memory, now, signals);
|
|
8894
|
+
const protectedMemory = isProtectedMemory(frontmatter, mergedPolicy);
|
|
8895
|
+
if (currentState === "archived") {
|
|
8896
|
+
return {
|
|
8897
|
+
currentState,
|
|
8898
|
+
nextState: "archived",
|
|
8899
|
+
heatScore,
|
|
8900
|
+
decayScore,
|
|
8901
|
+
changed: false,
|
|
8902
|
+
reason: "archived_is_terminal"
|
|
8903
|
+
};
|
|
8904
|
+
}
|
|
8905
|
+
if (frontmatter.verificationState === "disputed") {
|
|
8906
|
+
const nextState = toTerminalDisputedState(currentState);
|
|
8907
|
+
return {
|
|
8908
|
+
currentState,
|
|
8909
|
+
nextState,
|
|
8910
|
+
heatScore,
|
|
8911
|
+
decayScore,
|
|
8912
|
+
changed: nextState !== currentState,
|
|
8913
|
+
reason: "disputed_memories_do_not_promote_to_active"
|
|
8914
|
+
};
|
|
8915
|
+
}
|
|
8916
|
+
if (decayScore >= mergedPolicy.archiveDecayThreshold && !protectedMemory) {
|
|
8917
|
+
return {
|
|
8918
|
+
currentState,
|
|
8919
|
+
nextState: "archived",
|
|
8920
|
+
heatScore,
|
|
8921
|
+
decayScore,
|
|
8922
|
+
changed: true,
|
|
8923
|
+
reason: "decay_exceeded_archive_threshold"
|
|
8924
|
+
};
|
|
8925
|
+
}
|
|
8926
|
+
if (decayScore >= mergedPolicy.staleDecayThreshold) {
|
|
8927
|
+
return {
|
|
8928
|
+
currentState,
|
|
8929
|
+
nextState: "stale",
|
|
8930
|
+
heatScore,
|
|
8931
|
+
decayScore,
|
|
8932
|
+
changed: currentState !== "stale",
|
|
8933
|
+
reason: "decay_exceeded_stale_threshold"
|
|
8934
|
+
};
|
|
8935
|
+
}
|
|
8936
|
+
if (heatScore >= mergedPolicy.promoteHeatThreshold) {
|
|
8937
|
+
const nextState = isActiveEligible(frontmatter.verificationState) ? "active" : "validated";
|
|
8938
|
+
return {
|
|
8939
|
+
currentState,
|
|
8940
|
+
nextState,
|
|
8941
|
+
heatScore,
|
|
8942
|
+
decayScore,
|
|
8943
|
+
changed: currentState !== nextState,
|
|
8944
|
+
reason: "heat_exceeded_promote_threshold"
|
|
8945
|
+
};
|
|
8946
|
+
}
|
|
8947
|
+
return {
|
|
8948
|
+
currentState,
|
|
8949
|
+
nextState: currentState,
|
|
8950
|
+
heatScore,
|
|
8951
|
+
decayScore,
|
|
8952
|
+
changed: false,
|
|
8953
|
+
reason: "no_transition"
|
|
8954
|
+
};
|
|
8955
|
+
}
|
|
8956
|
+
|
|
8755
8957
|
// src/temporal-index.ts
|
|
8756
8958
|
import * as fs2 from "fs";
|
|
8757
8959
|
import * as path15 from "path";
|
|
@@ -9735,6 +9937,42 @@ function filterRecallCandidates(candidates, options) {
|
|
|
9735
9937
|
const scopedByNamespace = options.namespacesEnabled ? candidates.filter((r) => options.recallNamespaces.includes(options.resolveNamespace(r.path))) : candidates;
|
|
9736
9938
|
return scopedByNamespace.filter((r) => !isArtifactMemoryPath(r.path)).slice(0, Math.max(0, options.limit));
|
|
9737
9939
|
}
|
|
9940
|
+
function hasLifecycleMetadata(frontmatter) {
|
|
9941
|
+
return frontmatter.lifecycleState !== void 0 || frontmatter.verificationState !== void 0 || frontmatter.policyClass !== void 0 || frontmatter.lastValidatedAt !== void 0 || frontmatter.decayScore !== void 0 || frontmatter.heatScore !== void 0;
|
|
9942
|
+
}
|
|
9943
|
+
function shouldFilterLifecycleRecallCandidate(frontmatter, options) {
|
|
9944
|
+
if (!options.lifecyclePolicyEnabled || !options.lifecycleFilterStaleEnabled) return false;
|
|
9945
|
+
if (!hasLifecycleMetadata(frontmatter)) return false;
|
|
9946
|
+
const lifecycleState = resolveLifecycleState(frontmatter);
|
|
9947
|
+
return lifecycleState === "stale" || lifecycleState === "archived";
|
|
9948
|
+
}
|
|
9949
|
+
function lifecycleRecallScoreAdjustment(frontmatter, options) {
|
|
9950
|
+
if (!options.lifecyclePolicyEnabled) return 0;
|
|
9951
|
+
if (!hasLifecycleMetadata(frontmatter)) return 0;
|
|
9952
|
+
let delta = 0;
|
|
9953
|
+
const lifecycleState = resolveLifecycleState(frontmatter);
|
|
9954
|
+
switch (lifecycleState) {
|
|
9955
|
+
case "active":
|
|
9956
|
+
delta += 0.05;
|
|
9957
|
+
break;
|
|
9958
|
+
case "validated":
|
|
9959
|
+
delta += 0.03;
|
|
9960
|
+
break;
|
|
9961
|
+
case "candidate":
|
|
9962
|
+
delta -= 0.01;
|
|
9963
|
+
break;
|
|
9964
|
+
case "stale":
|
|
9965
|
+
delta -= 0.06;
|
|
9966
|
+
break;
|
|
9967
|
+
case "archived":
|
|
9968
|
+
delta -= 0.08;
|
|
9969
|
+
break;
|
|
9970
|
+
}
|
|
9971
|
+
if (frontmatter.verificationState === "disputed") {
|
|
9972
|
+
delta -= 0.12;
|
|
9973
|
+
}
|
|
9974
|
+
return delta;
|
|
9975
|
+
}
|
|
9738
9976
|
function computeArtifactRecallLimit(recallMode, recallResultLimit, verbatimArtifactsMaxRecall) {
|
|
9739
9977
|
if (recallMode === "no_recall") return 0;
|
|
9740
9978
|
if (Math.max(0, recallResultLimit) === 0) return 0;
|
|
@@ -11777,6 +12015,14 @@ _Context: ${topQuestion.context}_`);
|
|
|
11777
12015
|
}
|
|
11778
12016
|
}
|
|
11779
12017
|
}
|
|
12018
|
+
if (this.config.lifecyclePolicyEnabled) {
|
|
12019
|
+
try {
|
|
12020
|
+
const lifecycleCorpus = await this.storage.readAllMemories();
|
|
12021
|
+
await this.runLifecyclePolicyPass(lifecycleCorpus);
|
|
12022
|
+
} catch (err) {
|
|
12023
|
+
log.warn(`lifecycle policy pass failed (ignored): ${err}`);
|
|
12024
|
+
}
|
|
12025
|
+
}
|
|
11780
12026
|
if (this.config.factArchivalEnabled) {
|
|
11781
12027
|
const archived = await this.runFactArchival(allMemories);
|
|
11782
12028
|
if (archived > 0) {
|
|
@@ -11838,6 +12084,76 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
|
|
|
11838
12084
|
log.info("consolidation complete");
|
|
11839
12085
|
return { memoriesProcessed: allMemories.length, merged, invalidated };
|
|
11840
12086
|
}
|
|
12087
|
+
async runLifecyclePolicyPass(allMemories) {
|
|
12088
|
+
const now = /* @__PURE__ */ new Date();
|
|
12089
|
+
const nowIso = now.toISOString();
|
|
12090
|
+
const countsByState = {
|
|
12091
|
+
candidate: 0,
|
|
12092
|
+
validated: 0,
|
|
12093
|
+
active: 0,
|
|
12094
|
+
stale: 0,
|
|
12095
|
+
archived: 0
|
|
12096
|
+
};
|
|
12097
|
+
const transitionCounts = {};
|
|
12098
|
+
let updatedCount = 0;
|
|
12099
|
+
let disputedCount = 0;
|
|
12100
|
+
let evaluatedCount = 0;
|
|
12101
|
+
const policy = {
|
|
12102
|
+
promoteHeatThreshold: this.config.lifecyclePromoteHeatThreshold,
|
|
12103
|
+
staleDecayThreshold: this.config.lifecycleStaleDecayThreshold,
|
|
12104
|
+
archiveDecayThreshold: this.config.lifecycleArchiveDecayThreshold,
|
|
12105
|
+
protectedCategories: this.config.lifecycleProtectedCategories
|
|
12106
|
+
};
|
|
12107
|
+
for (const memory of allMemories) {
|
|
12108
|
+
if (memory.frontmatter.status === "superseded") {
|
|
12109
|
+
continue;
|
|
12110
|
+
}
|
|
12111
|
+
evaluatedCount += 1;
|
|
12112
|
+
const currentState = resolveLifecycleState(memory.frontmatter);
|
|
12113
|
+
const decision = decideLifecycleTransition(memory, policy, now);
|
|
12114
|
+
const nextState = memory.frontmatter.status === "archived" ? "archived" : decision.nextState;
|
|
12115
|
+
countsByState[nextState] += 1;
|
|
12116
|
+
if (memory.frontmatter.verificationState === "disputed") {
|
|
12117
|
+
disputedCount += 1;
|
|
12118
|
+
}
|
|
12119
|
+
if (nextState !== currentState) {
|
|
12120
|
+
const key = `${currentState}->${nextState}`;
|
|
12121
|
+
transitionCounts[key] = (transitionCounts[key] ?? 0) + 1;
|
|
12122
|
+
}
|
|
12123
|
+
const prevHeat = memory.frontmatter.heatScore;
|
|
12124
|
+
const prevDecay = memory.frontmatter.decayScore;
|
|
12125
|
+
const scoreDelta = Math.abs((prevHeat ?? -1) - decision.heatScore) + Math.abs((prevDecay ?? -1) - decision.decayScore);
|
|
12126
|
+
const shouldPersist = memory.frontmatter.lifecycleState !== nextState || memory.frontmatter.heatScore === void 0 || memory.frontmatter.decayScore === void 0 || memory.frontmatter.lastValidatedAt === void 0 || scoreDelta >= 0.01;
|
|
12127
|
+
if (!shouldPersist) continue;
|
|
12128
|
+
const wrote = await this.storage.writeMemoryFrontmatter(memory, {
|
|
12129
|
+
lifecycleState: nextState,
|
|
12130
|
+
heatScore: decision.heatScore,
|
|
12131
|
+
decayScore: decision.decayScore,
|
|
12132
|
+
lastValidatedAt: nowIso
|
|
12133
|
+
});
|
|
12134
|
+
if (wrote) updatedCount += 1;
|
|
12135
|
+
}
|
|
12136
|
+
if (!this.config.lifecycleMetricsEnabled) return;
|
|
12137
|
+
const total = evaluatedCount;
|
|
12138
|
+
const metrics = {
|
|
12139
|
+
generatedAt: nowIso,
|
|
12140
|
+
memoriesEvaluated: total,
|
|
12141
|
+
memoriesUpdated: updatedCount,
|
|
12142
|
+
countsByLifecycleState: countsByState,
|
|
12143
|
+
transitionCounts,
|
|
12144
|
+
staleRatio: total > 0 ? countsByState.stale / total : 0,
|
|
12145
|
+
disputedRatio: total > 0 ? disputedCount / total : 0,
|
|
12146
|
+
policy: {
|
|
12147
|
+
promoteHeatThreshold: this.config.lifecyclePromoteHeatThreshold,
|
|
12148
|
+
staleDecayThreshold: this.config.lifecycleStaleDecayThreshold,
|
|
12149
|
+
archiveDecayThreshold: this.config.lifecycleArchiveDecayThreshold,
|
|
12150
|
+
protectedCategories: this.config.lifecycleProtectedCategories
|
|
12151
|
+
}
|
|
12152
|
+
};
|
|
12153
|
+
const metricsPath = path22.join(this.storage.dir, "state", "lifecycle-metrics.json");
|
|
12154
|
+
await mkdir16(path22.dirname(metricsPath), { recursive: true });
|
|
12155
|
+
await writeFile15(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
|
|
12156
|
+
}
|
|
11841
12157
|
/**
|
|
11842
12158
|
* Archive old, low-importance, rarely-accessed facts (v6.0).
|
|
11843
12159
|
* Moves eligible facts from facts/ to archive/YYYY-MM-DD/.
|
|
@@ -12122,10 +12438,19 @@ ${lines.join("\n\n")}`;
|
|
|
12122
12438
|
tagCandidates = capSet(rawTags);
|
|
12123
12439
|
}
|
|
12124
12440
|
}
|
|
12125
|
-
|
|
12441
|
+
let lifecycleFilteredCount = 0;
|
|
12442
|
+
const boosted = [];
|
|
12443
|
+
for (const r of results) {
|
|
12126
12444
|
const memory = memoryByPath.get(r.path);
|
|
12127
12445
|
let score = r.score;
|
|
12128
12446
|
if (memory) {
|
|
12447
|
+
if (shouldFilterLifecycleRecallCandidate(memory.frontmatter, {
|
|
12448
|
+
lifecyclePolicyEnabled: this.config.lifecyclePolicyEnabled,
|
|
12449
|
+
lifecycleFilterStaleEnabled: this.config.lifecycleFilterStaleEnabled
|
|
12450
|
+
})) {
|
|
12451
|
+
lifecycleFilteredCount += 1;
|
|
12452
|
+
continue;
|
|
12453
|
+
}
|
|
12129
12454
|
if (this.config.recencyWeight > 0) {
|
|
12130
12455
|
const createdAt = new Date(memory.frontmatter.created).getTime();
|
|
12131
12456
|
const ageMs = now - createdAt;
|
|
@@ -12176,9 +12501,15 @@ ${lines.join("\n\n")}`;
|
|
|
12176
12501
|
score += 0.06;
|
|
12177
12502
|
}
|
|
12178
12503
|
}
|
|
12504
|
+
score += lifecycleRecallScoreAdjustment(memory.frontmatter, {
|
|
12505
|
+
lifecyclePolicyEnabled: this.config.lifecyclePolicyEnabled
|
|
12506
|
+
});
|
|
12179
12507
|
}
|
|
12180
|
-
|
|
12181
|
-
}
|
|
12508
|
+
boosted.push({ ...r, score });
|
|
12509
|
+
}
|
|
12510
|
+
if (lifecycleFilteredCount > 0) {
|
|
12511
|
+
log.debug(`lifecycle retrieval filter removed ${lifecycleFilteredCount} stale/archived candidates`);
|
|
12512
|
+
}
|
|
12182
12513
|
return boosted.sort((a, b) => b.score - a.score);
|
|
12183
12514
|
}
|
|
12184
12515
|
/**
|