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