@joshuaswarren/openclaw-engram 9.0.37 → 9.0.39
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/README.md +8 -0
- package/dist/index.js +864 -470
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +21 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -304,6 +304,8 @@ function parseConfig(raw) {
|
|
|
304
304
|
memoryRedTeamBenchEnabled: cfg.memoryRedTeamBenchEnabled === true,
|
|
305
305
|
harmonicRetrievalEnabled: cfg.harmonicRetrievalEnabled === true,
|
|
306
306
|
abstractionAnchorsEnabled: cfg.abstractionAnchorsEnabled === true,
|
|
307
|
+
verifiedRecallEnabled: cfg.verifiedRecallEnabled === true,
|
|
308
|
+
semanticRulePromotionEnabled: cfg.semanticRulePromotionEnabled === true,
|
|
307
309
|
abstractionNodeStoreDir: typeof cfg.abstractionNodeStoreDir === "string" && cfg.abstractionNodeStoreDir.trim().length > 0 ? cfg.abstractionNodeStoreDir.trim() : path.join(memoryDir, "state", "abstraction-nodes"),
|
|
308
310
|
// Local LLM Provider (v2.1)
|
|
309
311
|
localLlmEnabled: cfg.localLlmEnabled === true || cfg.localLlmEnabled === "true",
|
|
@@ -581,6 +583,18 @@ function buildDefaultRecallPipeline(cfg) {
|
|
|
581
583
|
maxResults: 3,
|
|
582
584
|
maxChars: 1800
|
|
583
585
|
},
|
|
586
|
+
{
|
|
587
|
+
id: "harmonic-retrieval",
|
|
588
|
+
enabled: cfg.harmonicRetrievalEnabled === true,
|
|
589
|
+
maxResults: 3,
|
|
590
|
+
maxChars: 2200
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
id: "verified-episodes",
|
|
594
|
+
enabled: cfg.verifiedRecallEnabled === true,
|
|
595
|
+
maxResults: 3,
|
|
596
|
+
maxChars: 1800
|
|
597
|
+
},
|
|
584
598
|
{
|
|
585
599
|
id: "memories",
|
|
586
600
|
enabled: true,
|
|
@@ -628,10 +642,10 @@ function buildRecallPipelineConfig(cfg) {
|
|
|
628
642
|
}
|
|
629
643
|
|
|
630
644
|
// src/orchestrator.ts
|
|
631
|
-
import
|
|
645
|
+
import path37 from "path";
|
|
632
646
|
import os5 from "os";
|
|
633
647
|
import { createHash as createHash6 } from "crypto";
|
|
634
|
-
import { mkdir as
|
|
648
|
+
import { mkdir as mkdir26, readdir as readdir16, readFile as readFile23, stat as stat7, unlink as unlink5, writeFile as writeFile26 } from "fs/promises";
|
|
635
649
|
|
|
636
650
|
// src/signal.ts
|
|
637
651
|
var BUILTIN_HIGH_PATTERNS = [
|
|
@@ -15021,6 +15035,401 @@ async function getTrustZoneStoreStatus(options) {
|
|
|
15021
15035
|
};
|
|
15022
15036
|
}
|
|
15023
15037
|
|
|
15038
|
+
// src/harmonic-retrieval.ts
|
|
15039
|
+
import path27 from "path";
|
|
15040
|
+
|
|
15041
|
+
// src/abstraction-nodes.ts
|
|
15042
|
+
import path25 from "path";
|
|
15043
|
+
import { mkdir as mkdir18, writeFile as writeFile19 } from "fs/promises";
|
|
15044
|
+
function validateKind2(raw) {
|
|
15045
|
+
const value = assertString2(raw, "kind");
|
|
15046
|
+
if (!["episode", "topic", "project", "workflow", "constraint"].includes(value)) {
|
|
15047
|
+
throw new Error("kind must be one of episode|topic|project|workflow|constraint");
|
|
15048
|
+
}
|
|
15049
|
+
return value;
|
|
15050
|
+
}
|
|
15051
|
+
function validateLevel(raw) {
|
|
15052
|
+
const value = assertString2(raw, "abstractionLevel");
|
|
15053
|
+
if (!["micro", "meso", "macro"].includes(value)) {
|
|
15054
|
+
throw new Error("abstractionLevel must be one of micro|meso|macro");
|
|
15055
|
+
}
|
|
15056
|
+
return value;
|
|
15057
|
+
}
|
|
15058
|
+
function resolveAbstractionNodeStoreDir(memoryDir, overrideDir) {
|
|
15059
|
+
if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
|
|
15060
|
+
return overrideDir.trim();
|
|
15061
|
+
}
|
|
15062
|
+
return path25.join(memoryDir, "state", "abstraction-nodes");
|
|
15063
|
+
}
|
|
15064
|
+
function validateAbstractionNode(raw) {
|
|
15065
|
+
if (!isRecord2(raw)) throw new Error("abstraction node must be an object");
|
|
15066
|
+
if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
|
|
15067
|
+
return {
|
|
15068
|
+
schemaVersion: 1,
|
|
15069
|
+
nodeId: assertSafePathSegment(assertString2(raw.nodeId, "nodeId"), "nodeId"),
|
|
15070
|
+
recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
|
|
15071
|
+
sessionKey: assertString2(raw.sessionKey, "sessionKey"),
|
|
15072
|
+
kind: validateKind2(raw.kind),
|
|
15073
|
+
abstractionLevel: validateLevel(raw.abstractionLevel),
|
|
15074
|
+
title: assertString2(raw.title, "title"),
|
|
15075
|
+
summary: assertString2(raw.summary, "summary"),
|
|
15076
|
+
sourceMemoryIds: optionalStringArray2(raw.sourceMemoryIds, "sourceMemoryIds"),
|
|
15077
|
+
entityRefs: optionalStringArray2(raw.entityRefs, "entityRefs"),
|
|
15078
|
+
tags: optionalStringArray2(raw.tags, "tags"),
|
|
15079
|
+
metadata: validateStringRecord(raw.metadata, "metadata")
|
|
15080
|
+
};
|
|
15081
|
+
}
|
|
15082
|
+
async function getAbstractionNodeStoreStatus(options) {
|
|
15083
|
+
const rootDir = resolveAbstractionNodeStoreDir(options.memoryDir, options.abstractionNodeStoreDir);
|
|
15084
|
+
const nodesDir = path25.join(rootDir, "nodes");
|
|
15085
|
+
const files = await listJsonFiles(nodesDir);
|
|
15086
|
+
const nodes = [];
|
|
15087
|
+
const invalidNodes = [];
|
|
15088
|
+
for (const filePath of files) {
|
|
15089
|
+
try {
|
|
15090
|
+
nodes.push(validateAbstractionNode(await readJsonFile(filePath)));
|
|
15091
|
+
} catch (error) {
|
|
15092
|
+
invalidNodes.push({
|
|
15093
|
+
path: filePath,
|
|
15094
|
+
error: error instanceof Error ? error.message : String(error)
|
|
15095
|
+
});
|
|
15096
|
+
}
|
|
15097
|
+
}
|
|
15098
|
+
nodes.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
|
|
15099
|
+
const byKind = {};
|
|
15100
|
+
const byLevel = {};
|
|
15101
|
+
for (const node of nodes) {
|
|
15102
|
+
byKind[node.kind] = (byKind[node.kind] ?? 0) + 1;
|
|
15103
|
+
byLevel[node.abstractionLevel] = (byLevel[node.abstractionLevel] ?? 0) + 1;
|
|
15104
|
+
}
|
|
15105
|
+
return {
|
|
15106
|
+
enabled: options.enabled,
|
|
15107
|
+
anchorsEnabled: options.anchorsEnabled,
|
|
15108
|
+
rootDir,
|
|
15109
|
+
nodesDir,
|
|
15110
|
+
nodes: {
|
|
15111
|
+
total: files.length,
|
|
15112
|
+
valid: nodes.length,
|
|
15113
|
+
invalid: invalidNodes.length,
|
|
15114
|
+
byKind,
|
|
15115
|
+
byLevel,
|
|
15116
|
+
latestNodeId: nodes[0]?.nodeId,
|
|
15117
|
+
latestRecordedAt: nodes[0]?.recordedAt,
|
|
15118
|
+
latestSessionKey: nodes[0]?.sessionKey
|
|
15119
|
+
},
|
|
15120
|
+
latestNode: nodes[0],
|
|
15121
|
+
invalidNodes
|
|
15122
|
+
};
|
|
15123
|
+
}
|
|
15124
|
+
|
|
15125
|
+
// src/cue-anchors.ts
|
|
15126
|
+
import path26 from "path";
|
|
15127
|
+
import { mkdir as mkdir19, writeFile as writeFile20 } from "fs/promises";
|
|
15128
|
+
function validateAnchorType(raw) {
|
|
15129
|
+
const value = assertString2(raw, "anchorType");
|
|
15130
|
+
if (!["entity", "file", "tool", "outcome", "constraint", "date"].includes(value)) {
|
|
15131
|
+
throw new Error("anchorType must be one of entity|file|tool|outcome|constraint|date");
|
|
15132
|
+
}
|
|
15133
|
+
return value;
|
|
15134
|
+
}
|
|
15135
|
+
function validateNodeRefs(raw) {
|
|
15136
|
+
const nodeRefs = optionalStringArray2(raw, "nodeRefs");
|
|
15137
|
+
if (!nodeRefs || nodeRefs.length === 0) {
|
|
15138
|
+
throw new Error("nodeRefs must contain at least one node reference");
|
|
15139
|
+
}
|
|
15140
|
+
return nodeRefs.map((nodeRef, index) => assertSafePathSegment(nodeRef, `nodeRefs[${index}]`));
|
|
15141
|
+
}
|
|
15142
|
+
function resolveCueAnchorStoreDir(abstractionNodeStoreDir, overrideDir) {
|
|
15143
|
+
if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
|
|
15144
|
+
return overrideDir.trim();
|
|
15145
|
+
}
|
|
15146
|
+
return path26.join(abstractionNodeStoreDir, "anchors");
|
|
15147
|
+
}
|
|
15148
|
+
function validateCueAnchor(raw) {
|
|
15149
|
+
if (!isRecord2(raw)) throw new Error("cue anchor must be an object");
|
|
15150
|
+
if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
|
|
15151
|
+
return {
|
|
15152
|
+
schemaVersion: 1,
|
|
15153
|
+
anchorId: assertSafePathSegment(assertString2(raw.anchorId, "anchorId"), "anchorId"),
|
|
15154
|
+
anchorType: validateAnchorType(raw.anchorType),
|
|
15155
|
+
anchorValue: assertString2(raw.anchorValue, "anchorValue"),
|
|
15156
|
+
normalizedCue: assertString2(raw.normalizedCue, "normalizedCue"),
|
|
15157
|
+
recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
|
|
15158
|
+
sessionKey: assertString2(raw.sessionKey, "sessionKey"),
|
|
15159
|
+
nodeRefs: validateNodeRefs(raw.nodeRefs),
|
|
15160
|
+
tags: optionalStringArray2(raw.tags, "tags"),
|
|
15161
|
+
metadata: validateStringRecord(raw.metadata, "metadata")
|
|
15162
|
+
};
|
|
15163
|
+
}
|
|
15164
|
+
async function getCueAnchorStoreStatus(options) {
|
|
15165
|
+
const abstractionNodeStoreDir = options.abstractionNodeStoreDir?.trim().length ? options.abstractionNodeStoreDir.trim() : path26.join(options.memoryDir, "state", "abstraction-nodes");
|
|
15166
|
+
const rootDir = resolveCueAnchorStoreDir(abstractionNodeStoreDir, options.cueAnchorStoreDir);
|
|
15167
|
+
const files = await listJsonFiles(rootDir);
|
|
15168
|
+
const anchors = [];
|
|
15169
|
+
const invalidAnchors = [];
|
|
15170
|
+
for (const filePath of files) {
|
|
15171
|
+
try {
|
|
15172
|
+
anchors.push(validateCueAnchor(await readJsonFile(filePath)));
|
|
15173
|
+
} catch (error) {
|
|
15174
|
+
invalidAnchors.push({
|
|
15175
|
+
path: filePath,
|
|
15176
|
+
error: error instanceof Error ? error.message : String(error)
|
|
15177
|
+
});
|
|
15178
|
+
}
|
|
15179
|
+
}
|
|
15180
|
+
anchors.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
|
|
15181
|
+
const byType = {};
|
|
15182
|
+
let totalNodeRefs = 0;
|
|
15183
|
+
for (const anchor of anchors) {
|
|
15184
|
+
byType[anchor.anchorType] = (byType[anchor.anchorType] ?? 0) + 1;
|
|
15185
|
+
totalNodeRefs += anchor.nodeRefs.length;
|
|
15186
|
+
}
|
|
15187
|
+
return {
|
|
15188
|
+
enabled: options.enabled,
|
|
15189
|
+
anchorsEnabled: options.anchorsEnabled,
|
|
15190
|
+
rootDir,
|
|
15191
|
+
anchors: {
|
|
15192
|
+
total: files.length,
|
|
15193
|
+
valid: anchors.length,
|
|
15194
|
+
invalid: invalidAnchors.length,
|
|
15195
|
+
byType,
|
|
15196
|
+
totalNodeRefs,
|
|
15197
|
+
latestAnchorId: anchors[0]?.anchorId,
|
|
15198
|
+
latestRecordedAt: anchors[0]?.recordedAt,
|
|
15199
|
+
latestSessionKey: anchors[0]?.sessionKey
|
|
15200
|
+
},
|
|
15201
|
+
latestAnchor: anchors[0],
|
|
15202
|
+
invalidAnchors
|
|
15203
|
+
};
|
|
15204
|
+
}
|
|
15205
|
+
|
|
15206
|
+
// src/harmonic-retrieval.ts
|
|
15207
|
+
function scoreNode(node, queryTokens) {
|
|
15208
|
+
const matchedFields = [];
|
|
15209
|
+
let score = 0;
|
|
15210
|
+
const titleMatches = countRecallTokenOverlap(queryTokens, node.title);
|
|
15211
|
+
if (titleMatches > 0) {
|
|
15212
|
+
score += titleMatches * 3;
|
|
15213
|
+
matchedFields.push("title");
|
|
15214
|
+
}
|
|
15215
|
+
const summaryMatches = countRecallTokenOverlap(queryTokens, node.summary);
|
|
15216
|
+
if (summaryMatches > 0) {
|
|
15217
|
+
score += summaryMatches * 3;
|
|
15218
|
+
matchedFields.push("summary");
|
|
15219
|
+
}
|
|
15220
|
+
const tagMatches = countRecallTokenOverlap(queryTokens, node.tags?.join(" "));
|
|
15221
|
+
if (tagMatches > 0) {
|
|
15222
|
+
score += tagMatches * 2;
|
|
15223
|
+
matchedFields.push("tags");
|
|
15224
|
+
}
|
|
15225
|
+
const entityMatches = countRecallTokenOverlap(queryTokens, node.entityRefs?.join(" "));
|
|
15226
|
+
if (entityMatches > 0) {
|
|
15227
|
+
score += entityMatches * 2;
|
|
15228
|
+
matchedFields.push("entityRefs");
|
|
15229
|
+
}
|
|
15230
|
+
const kindMatches = countRecallTokenOverlap(queryTokens, `${node.kind} ${node.abstractionLevel}`);
|
|
15231
|
+
if (kindMatches > 0) {
|
|
15232
|
+
score += kindMatches;
|
|
15233
|
+
matchedFields.push("kind");
|
|
15234
|
+
}
|
|
15235
|
+
return { score, matchedFields };
|
|
15236
|
+
}
|
|
15237
|
+
function scoreAnchor(anchor, queryTokens) {
|
|
15238
|
+
const matchedFields = [];
|
|
15239
|
+
let score = 0;
|
|
15240
|
+
const valueMatches = countRecallTokenOverlap(queryTokens, anchor.anchorValue);
|
|
15241
|
+
const normalizedMatches = countRecallTokenOverlap(queryTokens, anchor.normalizedCue);
|
|
15242
|
+
const cueMatches = Math.max(valueMatches, normalizedMatches);
|
|
15243
|
+
if (cueMatches > 0) {
|
|
15244
|
+
score += cueMatches * 4;
|
|
15245
|
+
if (valueMatches > 0) matchedFields.push("anchorValue");
|
|
15246
|
+
if (normalizedMatches > 0) matchedFields.push("anchor");
|
|
15247
|
+
}
|
|
15248
|
+
const typeMatches = countRecallTokenOverlap(queryTokens, anchor.anchorType);
|
|
15249
|
+
if (typeMatches > 0) {
|
|
15250
|
+
score += typeMatches;
|
|
15251
|
+
matchedFields.push("anchorType");
|
|
15252
|
+
}
|
|
15253
|
+
const tagMatches = countRecallTokenOverlap(queryTokens, anchor.tags?.join(" "));
|
|
15254
|
+
if (tagMatches > 0) {
|
|
15255
|
+
score += tagMatches * 2;
|
|
15256
|
+
matchedFields.push("anchorTags");
|
|
15257
|
+
}
|
|
15258
|
+
return { score, matchedFields };
|
|
15259
|
+
}
|
|
15260
|
+
async function readAbstractionNodes(options) {
|
|
15261
|
+
const rootDir = resolveAbstractionNodeStoreDir(options.memoryDir, options.abstractionNodeStoreDir);
|
|
15262
|
+
const files = await listJsonFiles(path27.join(rootDir, "nodes"));
|
|
15263
|
+
const nodes = [];
|
|
15264
|
+
for (const filePath of files) {
|
|
15265
|
+
try {
|
|
15266
|
+
nodes.push(validateAbstractionNode(await readJsonFile(filePath)));
|
|
15267
|
+
} catch {
|
|
15268
|
+
}
|
|
15269
|
+
}
|
|
15270
|
+
return nodes;
|
|
15271
|
+
}
|
|
15272
|
+
async function readCueAnchors(options) {
|
|
15273
|
+
const abstractionRoot = resolveAbstractionNodeStoreDir(options.memoryDir, options.abstractionNodeStoreDir);
|
|
15274
|
+
const rootDir = resolveCueAnchorStoreDir(abstractionRoot);
|
|
15275
|
+
const files = await listJsonFiles(rootDir);
|
|
15276
|
+
const anchors = [];
|
|
15277
|
+
for (const filePath of files) {
|
|
15278
|
+
try {
|
|
15279
|
+
anchors.push(validateCueAnchor(await readJsonFile(filePath)));
|
|
15280
|
+
} catch {
|
|
15281
|
+
}
|
|
15282
|
+
}
|
|
15283
|
+
return anchors;
|
|
15284
|
+
}
|
|
15285
|
+
async function searchHarmonicRetrieval(options) {
|
|
15286
|
+
const queryTokens = new Set(normalizeRecallTokens(options.query, ["what", "which"]));
|
|
15287
|
+
if (queryTokens.size === 0 || options.maxResults <= 0) return [];
|
|
15288
|
+
const nodes = await readAbstractionNodes(options);
|
|
15289
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
15290
|
+
for (const node of nodes) {
|
|
15291
|
+
const { score, matchedFields } = scoreNode(node, queryTokens);
|
|
15292
|
+
if (score <= 0) continue;
|
|
15293
|
+
candidates.set(node.nodeId, {
|
|
15294
|
+
node,
|
|
15295
|
+
nodeScore: score,
|
|
15296
|
+
anchorScore: 0,
|
|
15297
|
+
matchedFields: new Set(matchedFields),
|
|
15298
|
+
matchedAnchors: /* @__PURE__ */ new Map()
|
|
15299
|
+
});
|
|
15300
|
+
}
|
|
15301
|
+
if (options.anchorsEnabled) {
|
|
15302
|
+
const anchors = await readCueAnchors(options);
|
|
15303
|
+
const nodeIndex = new Map(nodes.map((node) => [node.nodeId, node]));
|
|
15304
|
+
for (const anchor of anchors) {
|
|
15305
|
+
const { score, matchedFields } = scoreAnchor(anchor, queryTokens);
|
|
15306
|
+
if (score <= 0) continue;
|
|
15307
|
+
for (const nodeRef of anchor.nodeRefs) {
|
|
15308
|
+
const node = nodeIndex.get(nodeRef);
|
|
15309
|
+
if (!node) continue;
|
|
15310
|
+
const existing = candidates.get(nodeRef) ?? {
|
|
15311
|
+
node,
|
|
15312
|
+
nodeScore: 0,
|
|
15313
|
+
anchorScore: 0,
|
|
15314
|
+
matchedFields: /* @__PURE__ */ new Set(),
|
|
15315
|
+
matchedAnchors: /* @__PURE__ */ new Map()
|
|
15316
|
+
};
|
|
15317
|
+
existing.anchorScore += score;
|
|
15318
|
+
existing.matchedFields.add("anchor");
|
|
15319
|
+
for (const field of matchedFields) existing.matchedFields.add(field);
|
|
15320
|
+
existing.matchedAnchors.set(anchor.anchorId, {
|
|
15321
|
+
anchorId: anchor.anchorId,
|
|
15322
|
+
anchorType: anchor.anchorType,
|
|
15323
|
+
anchorValue: anchor.anchorValue
|
|
15324
|
+
});
|
|
15325
|
+
candidates.set(nodeRef, existing);
|
|
15326
|
+
}
|
|
15327
|
+
}
|
|
15328
|
+
}
|
|
15329
|
+
return [...candidates.values()].map((candidate) => {
|
|
15330
|
+
let score = candidate.nodeScore + candidate.anchorScore;
|
|
15331
|
+
if (options.sessionKey && candidate.node.sessionKey === options.sessionKey) score += 0.5;
|
|
15332
|
+
return {
|
|
15333
|
+
node: candidate.node,
|
|
15334
|
+
score,
|
|
15335
|
+
nodeScore: candidate.nodeScore,
|
|
15336
|
+
anchorScore: candidate.anchorScore,
|
|
15337
|
+
matchedFields: [...candidate.matchedFields].sort(),
|
|
15338
|
+
matchedAnchors: [...candidate.matchedAnchors.values()].sort(
|
|
15339
|
+
(left, right) => left.anchorType.localeCompare(right.anchorType) || left.anchorValue.localeCompare(right.anchorValue)
|
|
15340
|
+
)
|
|
15341
|
+
};
|
|
15342
|
+
}).filter((result) => result.nodeScore > 0 || result.anchorScore > 0).sort(
|
|
15343
|
+
(left, right) => right.score - left.score || right.anchorScore - left.anchorScore || right.node.recordedAt.localeCompare(left.node.recordedAt)
|
|
15344
|
+
).slice(0, options.maxResults);
|
|
15345
|
+
}
|
|
15346
|
+
|
|
15347
|
+
// src/verified-recall.ts
|
|
15348
|
+
function createReadOnlyBoxBuilder(memoryDir) {
|
|
15349
|
+
return new BoxBuilder(memoryDir, {
|
|
15350
|
+
memoryBoxesEnabled: true,
|
|
15351
|
+
traceWeaverEnabled: false,
|
|
15352
|
+
boxTopicShiftThreshold: 0.35,
|
|
15353
|
+
boxTimeGapMs: 30 * 60 * 1e3,
|
|
15354
|
+
boxMaxMemories: 50,
|
|
15355
|
+
traceWeaverLookbackDays: 7,
|
|
15356
|
+
traceWeaverOverlapThreshold: 0.4
|
|
15357
|
+
});
|
|
15358
|
+
}
|
|
15359
|
+
function scoreVerifiedEpisodeCandidate(box, verifiedMemories, queryTokens) {
|
|
15360
|
+
const matchedFields = /* @__PURE__ */ new Set();
|
|
15361
|
+
let score = 0;
|
|
15362
|
+
const topicMatches = countRecallTokenOverlap(queryTokens, box.topics.join(" "));
|
|
15363
|
+
if (topicMatches > 0) {
|
|
15364
|
+
score += topicMatches * 3;
|
|
15365
|
+
matchedFields.add("topics");
|
|
15366
|
+
}
|
|
15367
|
+
const goalMatches = countRecallTokenOverlap(queryTokens, box.goal);
|
|
15368
|
+
if (goalMatches > 0) {
|
|
15369
|
+
score += goalMatches * 4;
|
|
15370
|
+
matchedFields.add("goal");
|
|
15371
|
+
}
|
|
15372
|
+
const toolMatches = countRecallTokenOverlap(queryTokens, box.toolsUsed?.join(" "));
|
|
15373
|
+
if (toolMatches > 0) {
|
|
15374
|
+
score += toolMatches * 2;
|
|
15375
|
+
matchedFields.add("toolsUsed");
|
|
15376
|
+
}
|
|
15377
|
+
let episodeContentMatches = 0;
|
|
15378
|
+
for (const memory of verifiedMemories) {
|
|
15379
|
+
episodeContentMatches += countRecallTokenOverlap(queryTokens, memory.content);
|
|
15380
|
+
}
|
|
15381
|
+
if (episodeContentMatches > 0) {
|
|
15382
|
+
score += episodeContentMatches * 4;
|
|
15383
|
+
matchedFields.add("episodeContent");
|
|
15384
|
+
}
|
|
15385
|
+
return { score, matchedFields };
|
|
15386
|
+
}
|
|
15387
|
+
function resolveVerifiedEpisodeMemoriesFromMap(memoryById, memoryIds) {
|
|
15388
|
+
const verified = [];
|
|
15389
|
+
for (const memoryId of memoryIds) {
|
|
15390
|
+
try {
|
|
15391
|
+
const memory = memoryById.get(memoryId);
|
|
15392
|
+
if (!memory) continue;
|
|
15393
|
+
if (memory.frontmatter.status === "archived") continue;
|
|
15394
|
+
if (memory.frontmatter.memoryKind !== "episode") continue;
|
|
15395
|
+
verified.push(memory);
|
|
15396
|
+
} catch {
|
|
15397
|
+
}
|
|
15398
|
+
}
|
|
15399
|
+
return verified;
|
|
15400
|
+
}
|
|
15401
|
+
async function searchVerifiedEpisodes(options) {
|
|
15402
|
+
const queryTokens = new Set(normalizeRecallTokens(options.query, ["what", "which"]));
|
|
15403
|
+
if (queryTokens.size === 0 || options.maxResults <= 0) return [];
|
|
15404
|
+
const storage = new StorageManager(options.memoryDir);
|
|
15405
|
+
const verifiedMemoryById = new Map(
|
|
15406
|
+
(await storage.readAllMemories()).filter((memory) => memory.frontmatter.status !== "archived").filter((memory) => memory.frontmatter.memoryKind === "episode").map((memory) => [memory.frontmatter.id, memory])
|
|
15407
|
+
);
|
|
15408
|
+
const boxes = await createReadOnlyBoxBuilder(options.memoryDir).readRecentBoxes(Math.max(1, Math.floor(options.boxRecallDays ?? 3))).catch(() => []);
|
|
15409
|
+
const candidates = [];
|
|
15410
|
+
for (const box of boxes) {
|
|
15411
|
+
const verifiedMemories = resolveVerifiedEpisodeMemoriesFromMap(verifiedMemoryById, box.memoryIds);
|
|
15412
|
+
if (verifiedMemories.length === 0) continue;
|
|
15413
|
+
const { score, matchedFields } = scoreVerifiedEpisodeCandidate(box, verifiedMemories, queryTokens);
|
|
15414
|
+
if (score <= 0) continue;
|
|
15415
|
+
candidates.push({
|
|
15416
|
+
box,
|
|
15417
|
+
score,
|
|
15418
|
+
matchedFields,
|
|
15419
|
+
verifiedMemories
|
|
15420
|
+
});
|
|
15421
|
+
}
|
|
15422
|
+
return candidates.map((candidate) => ({
|
|
15423
|
+
box: candidate.box,
|
|
15424
|
+
score: candidate.score,
|
|
15425
|
+
verifiedEpisodeCount: candidate.verifiedMemories.length,
|
|
15426
|
+
verifiedMemoryIds: candidate.verifiedMemories.map((memory) => memory.frontmatter.id),
|
|
15427
|
+
matchedFields: [...candidate.matchedFields].sort()
|
|
15428
|
+
})).sort(
|
|
15429
|
+
(left, right) => right.score - left.score || right.verifiedEpisodeCount - left.verifiedEpisodeCount || right.box.sealedAt.localeCompare(left.box.sealedAt)
|
|
15430
|
+
).slice(0, options.maxResults);
|
|
15431
|
+
}
|
|
15432
|
+
|
|
15024
15433
|
// src/replay/types.ts
|
|
15025
15434
|
var VALID_SOURCES = /* @__PURE__ */ new Set(["openclaw", "claude", "chatgpt"]);
|
|
15026
15435
|
var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
@@ -15129,8 +15538,8 @@ function chunkTranscriptEntries(sessionKey, entries, opts) {
|
|
|
15129
15538
|
}
|
|
15130
15539
|
|
|
15131
15540
|
// src/conversation-index/indexer.ts
|
|
15132
|
-
import { mkdir as
|
|
15133
|
-
import
|
|
15541
|
+
import { mkdir as mkdir20, writeFile as writeFile21 } from "fs/promises";
|
|
15542
|
+
import path28 from "path";
|
|
15134
15543
|
function sanitizeSessionKey(sessionKey) {
|
|
15135
15544
|
const raw = typeof sessionKey === "string" && sessionKey.trim().length > 0 ? sessionKey : "unknown-session";
|
|
15136
15545
|
return raw.toLowerCase().replace(/[^a-z0-9._-]+/g, "_").slice(0, 200);
|
|
@@ -15140,9 +15549,9 @@ async function writeConversationChunks(rootDir, chunks) {
|
|
|
15140
15549
|
for (const c of chunks) {
|
|
15141
15550
|
const safe = sanitizeSessionKey(c.sessionKey);
|
|
15142
15551
|
const date = c.startTs.slice(0, 10);
|
|
15143
|
-
const dir =
|
|
15144
|
-
await
|
|
15145
|
-
const fp =
|
|
15552
|
+
const dir = path28.join(rootDir, safe, date);
|
|
15553
|
+
await mkdir20(dir, { recursive: true });
|
|
15554
|
+
const fp = path28.join(dir, `${c.id}.md`);
|
|
15146
15555
|
const content = `---
|
|
15147
15556
|
kind: conversation_chunk
|
|
15148
15557
|
sessionKey: ${c.sessionKey}
|
|
@@ -15151,7 +15560,7 @@ endTs: ${c.endTs}
|
|
|
15151
15560
|
---
|
|
15152
15561
|
|
|
15153
15562
|
` + c.text + "\n";
|
|
15154
|
-
await
|
|
15563
|
+
await writeFile21(fp, content, "utf-8");
|
|
15155
15564
|
written.push(fp);
|
|
15156
15565
|
}
|
|
15157
15566
|
return written;
|
|
@@ -15171,7 +15580,7 @@ async function upsertConversationChunksFailOpen(adapter, chunks) {
|
|
|
15171
15580
|
|
|
15172
15581
|
// src/conversation-index/cleanup.ts
|
|
15173
15582
|
import { readdir as readdir13, rm as rm2 } from "fs/promises";
|
|
15174
|
-
import
|
|
15583
|
+
import path29 from "path";
|
|
15175
15584
|
async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
15176
15585
|
if (!Number.isFinite(retentionDays) || retentionDays <= 0) return;
|
|
15177
15586
|
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
@@ -15179,7 +15588,7 @@ async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
|
15179
15588
|
const sessions = await readdir13(rootDir, { withFileTypes: true });
|
|
15180
15589
|
for (const s of sessions) {
|
|
15181
15590
|
if (!s.isDirectory()) continue;
|
|
15182
|
-
const sessionDir =
|
|
15591
|
+
const sessionDir = path29.join(rootDir, s.name);
|
|
15183
15592
|
const dayDirs = await readdir13(sessionDir, { withFileTypes: true });
|
|
15184
15593
|
for (const d of dayDirs) {
|
|
15185
15594
|
if (!d.isDirectory()) continue;
|
|
@@ -15187,7 +15596,7 @@ async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
|
15187
15596
|
const dayMs = (/* @__PURE__ */ new Date(d.name + "T00:00:00.000Z")).getTime();
|
|
15188
15597
|
if (!Number.isFinite(dayMs)) continue;
|
|
15189
15598
|
if (dayMs < cutoffMs) {
|
|
15190
|
-
await rm2(
|
|
15599
|
+
await rm2(path29.join(sessionDir, d.name), { recursive: true, force: true });
|
|
15191
15600
|
}
|
|
15192
15601
|
}
|
|
15193
15602
|
try {
|
|
@@ -15206,7 +15615,7 @@ async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
|
15206
15615
|
// src/conversation-index/faiss-adapter.ts
|
|
15207
15616
|
import * as childProcess from "child_process";
|
|
15208
15617
|
import { fileURLToPath } from "url";
|
|
15209
|
-
import
|
|
15618
|
+
import path30 from "path";
|
|
15210
15619
|
var FaissAdapterError = class extends Error {
|
|
15211
15620
|
constructor(message, code) {
|
|
15212
15621
|
super(message);
|
|
@@ -15216,18 +15625,18 @@ var FaissAdapterError = class extends Error {
|
|
|
15216
15625
|
};
|
|
15217
15626
|
function resolveDefaultFaissScriptPath(fromModuleUrl = import.meta.url) {
|
|
15218
15627
|
const currentFile = fileURLToPath(fromModuleUrl);
|
|
15219
|
-
const moduleDir =
|
|
15220
|
-
if (moduleDir.endsWith(`${
|
|
15221
|
-
return
|
|
15628
|
+
const moduleDir = path30.dirname(currentFile);
|
|
15629
|
+
if (moduleDir.endsWith(`${path30.sep}conversation-index`)) {
|
|
15630
|
+
return path30.resolve(moduleDir, "..", "..", "scripts", "faiss_index.py");
|
|
15222
15631
|
}
|
|
15223
|
-
return
|
|
15632
|
+
return path30.resolve(moduleDir, "..", "scripts", "faiss_index.py");
|
|
15224
15633
|
}
|
|
15225
15634
|
var FaissConversationIndexAdapter = class {
|
|
15226
15635
|
constructor(config) {
|
|
15227
15636
|
this.config = config;
|
|
15228
15637
|
this.pythonBin = config.pythonBin && config.pythonBin.trim().length > 0 ? config.pythonBin.trim() : "python3";
|
|
15229
15638
|
this.scriptPath = config.scriptPath && config.scriptPath.trim().length > 0 ? config.scriptPath.trim() : resolveDefaultFaissScriptPath();
|
|
15230
|
-
this.indexPath =
|
|
15639
|
+
this.indexPath = path30.isAbsolute(config.indexDir) ? config.indexDir : path30.join(config.memoryDir, config.indexDir);
|
|
15231
15640
|
this.spawnFn = config.spawnFn ?? childProcess.spawn;
|
|
15232
15641
|
}
|
|
15233
15642
|
pythonBin;
|
|
@@ -15408,7 +15817,7 @@ async function searchConversationIndexFaissFailOpen(adapter, query, maxResults)
|
|
|
15408
15817
|
}
|
|
15409
15818
|
|
|
15410
15819
|
// src/namespaces/storage.ts
|
|
15411
|
-
import
|
|
15820
|
+
import path31 from "path";
|
|
15412
15821
|
import { access } from "fs/promises";
|
|
15413
15822
|
async function exists(p) {
|
|
15414
15823
|
try {
|
|
@@ -15430,7 +15839,7 @@ var NamespaceStorageRouter = class {
|
|
|
15430
15839
|
this.defaultNsRootResolved = this.config.memoryDir;
|
|
15431
15840
|
return this.defaultNsRootResolved;
|
|
15432
15841
|
}
|
|
15433
|
-
const nsDir =
|
|
15842
|
+
const nsDir = path31.join(this.config.memoryDir, "namespaces", this.config.defaultNamespace);
|
|
15434
15843
|
this.defaultNsRootResolved = await exists(nsDir) ? nsDir : this.config.memoryDir;
|
|
15435
15844
|
return this.defaultNsRootResolved;
|
|
15436
15845
|
}
|
|
@@ -15439,7 +15848,7 @@ var NamespaceStorageRouter = class {
|
|
|
15439
15848
|
if (namespace === this.config.defaultNamespace) {
|
|
15440
15849
|
return this.defaultNsRootResolved ?? this.config.memoryDir;
|
|
15441
15850
|
}
|
|
15442
|
-
return
|
|
15851
|
+
return path31.join(this.config.memoryDir, "namespaces", namespace);
|
|
15443
15852
|
}
|
|
15444
15853
|
async storageFor(namespace) {
|
|
15445
15854
|
const ns = namespace || this.config.defaultNamespace;
|
|
@@ -15515,8 +15924,8 @@ function recallNamespacesForPrincipal(principal, config) {
|
|
|
15515
15924
|
}
|
|
15516
15925
|
|
|
15517
15926
|
// src/shared-context/manager.ts
|
|
15518
|
-
import { mkdir as
|
|
15519
|
-
import
|
|
15927
|
+
import { mkdir as mkdir21, readFile as readFile19, readdir as readdir14, appendFile as appendFile4, writeFile as writeFile22, stat as stat5 } from "fs/promises";
|
|
15928
|
+
import path32 from "path";
|
|
15520
15929
|
import os3 from "os";
|
|
15521
15930
|
import { z as z3 } from "zod";
|
|
15522
15931
|
var SharedFeedbackEntrySchema = z3.object({
|
|
@@ -15712,15 +16121,15 @@ async function computeSemanticOverlapsWithTimeout(sources, timeoutMs, maxCandida
|
|
|
15712
16121
|
var SharedContextManager = class {
|
|
15713
16122
|
constructor(config) {
|
|
15714
16123
|
this.config = config;
|
|
15715
|
-
const base = typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0 ? config.sharedContextDir :
|
|
16124
|
+
const base = typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0 ? config.sharedContextDir : path32.join(os3.homedir(), ".openclaw", "workspace", "shared-context");
|
|
15716
16125
|
this.dir = base;
|
|
15717
|
-
this.prioritiesPath =
|
|
15718
|
-
this.prioritiesInboxPath =
|
|
15719
|
-
this.outputsDir =
|
|
15720
|
-
this.roundtableDir =
|
|
15721
|
-
this.feedbackDir =
|
|
15722
|
-
this.feedbackInboxPath =
|
|
15723
|
-
this.crossSignalsDir =
|
|
16126
|
+
this.prioritiesPath = path32.join(base, "priorities.md");
|
|
16127
|
+
this.prioritiesInboxPath = path32.join(base, "priorities.inbox.md");
|
|
16128
|
+
this.outputsDir = path32.join(base, "agent-outputs");
|
|
16129
|
+
this.roundtableDir = path32.join(base, "roundtable");
|
|
16130
|
+
this.feedbackDir = path32.join(base, "feedback");
|
|
16131
|
+
this.feedbackInboxPath = path32.join(this.feedbackDir, "inbox.jsonl");
|
|
16132
|
+
this.crossSignalsDir = path32.join(base, "cross-signals");
|
|
15724
16133
|
}
|
|
15725
16134
|
dir;
|
|
15726
16135
|
prioritiesPath;
|
|
@@ -15731,15 +16140,15 @@ var SharedContextManager = class {
|
|
|
15731
16140
|
feedbackInboxPath;
|
|
15732
16141
|
crossSignalsDir;
|
|
15733
16142
|
async ensureStructure() {
|
|
15734
|
-
await
|
|
15735
|
-
await
|
|
15736
|
-
await
|
|
15737
|
-
await
|
|
15738
|
-
await
|
|
15739
|
-
await
|
|
15740
|
-
await
|
|
15741
|
-
await
|
|
15742
|
-
await
|
|
16143
|
+
await mkdir21(this.dir, { recursive: true });
|
|
16144
|
+
await mkdir21(this.outputsDir, { recursive: true });
|
|
16145
|
+
await mkdir21(this.roundtableDir, { recursive: true });
|
|
16146
|
+
await mkdir21(this.feedbackDir, { recursive: true });
|
|
16147
|
+
await mkdir21(this.crossSignalsDir, { recursive: true });
|
|
16148
|
+
await mkdir21(path32.join(this.dir, "staging"), { recursive: true });
|
|
16149
|
+
await mkdir21(path32.join(this.dir, "kpis"), { recursive: true });
|
|
16150
|
+
await mkdir21(path32.join(this.dir, "calendar"), { recursive: true });
|
|
16151
|
+
await mkdir21(path32.join(this.dir, "content-calendar"), { recursive: true });
|
|
15743
16152
|
await this.ensureFile(
|
|
15744
16153
|
this.prioritiesPath,
|
|
15745
16154
|
[
|
|
@@ -15770,7 +16179,7 @@ var SharedContextManager = class {
|
|
|
15770
16179
|
try {
|
|
15771
16180
|
await stat5(fp);
|
|
15772
16181
|
} catch {
|
|
15773
|
-
await
|
|
16182
|
+
await writeFile22(fp, content, "utf-8");
|
|
15774
16183
|
}
|
|
15775
16184
|
}
|
|
15776
16185
|
async readPriorities() {
|
|
@@ -15783,7 +16192,7 @@ var SharedContextManager = class {
|
|
|
15783
16192
|
async readLatestRoundtable() {
|
|
15784
16193
|
try {
|
|
15785
16194
|
const files = (await readdir14(this.roundtableDir)).filter((f) => f.endsWith(".md")).sort().reverse();
|
|
15786
|
-
const fp = files[0] ?
|
|
16195
|
+
const fp = files[0] ? path32.join(this.roundtableDir, files[0]) : null;
|
|
15787
16196
|
if (!fp) return "";
|
|
15788
16197
|
return await readFile19(fp, "utf-8");
|
|
15789
16198
|
} catch {
|
|
@@ -15795,9 +16204,9 @@ var SharedContextManager = class {
|
|
|
15795
16204
|
const date = ymd(createdAt);
|
|
15796
16205
|
const time = createdAt.toISOString().slice(11, 19).replace(/:/g, "");
|
|
15797
16206
|
const slug = safeSlug(opts.title);
|
|
15798
|
-
const dir =
|
|
15799
|
-
await
|
|
15800
|
-
const fp =
|
|
16207
|
+
const dir = path32.join(this.outputsDir, opts.agentId, date);
|
|
16208
|
+
await mkdir21(dir, { recursive: true });
|
|
16209
|
+
const fp = path32.join(dir, `${time}-${slug}.md`);
|
|
15801
16210
|
const body = `---
|
|
15802
16211
|
kind: agent_output
|
|
15803
16212
|
agent: ${opts.agentId}
|
|
@@ -15806,7 +16215,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
|
|
|
15806
16215
|
---
|
|
15807
16216
|
|
|
15808
16217
|
` + opts.content.trimEnd() + "\n";
|
|
15809
|
-
await
|
|
16218
|
+
await writeFile22(fp, body, "utf-8");
|
|
15810
16219
|
return fp;
|
|
15811
16220
|
}
|
|
15812
16221
|
async appendFeedback(entry) {
|
|
@@ -15832,11 +16241,11 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
|
|
|
15832
16241
|
const agents = await readdir14(this.outputsDir, { withFileTypes: true });
|
|
15833
16242
|
for (const a of agents) {
|
|
15834
16243
|
if (!a.isDirectory()) continue;
|
|
15835
|
-
const dayDir =
|
|
16244
|
+
const dayDir = path32.join(this.outputsDir, a.name, date);
|
|
15836
16245
|
try {
|
|
15837
16246
|
const files = (await readdir14(dayDir)).filter((f) => f.endsWith(".md")).sort();
|
|
15838
16247
|
for (const f of files) {
|
|
15839
|
-
const p =
|
|
16248
|
+
const p = path32.join(dayDir, f);
|
|
15840
16249
|
const raw = await readFile19(p, "utf-8");
|
|
15841
16250
|
const title = (raw.match(/^title:\s*(.+)$/m)?.[1] ?? f).trim();
|
|
15842
16251
|
outputs.push({ agent: a.name, path: p, title, raw });
|
|
@@ -15943,8 +16352,8 @@ ${body}`)
|
|
|
15943
16352
|
addedOverlapCount: semanticAddedOverlapCount
|
|
15944
16353
|
}
|
|
15945
16354
|
};
|
|
15946
|
-
const crossSignalsPath =
|
|
15947
|
-
await
|
|
16355
|
+
const crossSignalsPath = path32.join(this.crossSignalsDir, `${date}.json`);
|
|
16356
|
+
await writeFile22(crossSignalsPath, `${JSON.stringify(crossSignalReport, null, 2)}
|
|
15948
16357
|
`, "utf-8");
|
|
15949
16358
|
const overlapBullets = mergedOverlaps.length === 0 ? ["- No multi-agent topic overlap detected."] : mergedOverlaps.slice(0, 8).map((entry) => `- \`${entry.token}\` (${entry.agentCount} agents: ${entry.agents.join(", ")})`);
|
|
15950
16359
|
const md = [
|
|
@@ -15967,8 +16376,8 @@ ${body}`)
|
|
|
15967
16376
|
];
|
|
15968
16377
|
const out = md.join("\n");
|
|
15969
16378
|
const trimmed = out.length > maxChars ? out.slice(0, maxChars) + "\n\n...(trimmed)\n" : out;
|
|
15970
|
-
const roundtablePath =
|
|
15971
|
-
await
|
|
16379
|
+
const roundtablePath = path32.join(this.roundtableDir, `${date}.md`);
|
|
16380
|
+
await writeFile22(roundtablePath, trimmed, "utf-8");
|
|
15972
16381
|
log.info(`shared-context curated daily roundtable: ${roundtablePath}`);
|
|
15973
16382
|
return {
|
|
15974
16383
|
date,
|
|
@@ -15980,8 +16389,8 @@ ${body}`)
|
|
|
15980
16389
|
};
|
|
15981
16390
|
|
|
15982
16391
|
// src/compounding/engine.ts
|
|
15983
|
-
import { mkdir as
|
|
15984
|
-
import
|
|
16392
|
+
import { mkdir as mkdir22, readFile as readFile20, readdir as readdir15, writeFile as writeFile23 } from "fs/promises";
|
|
16393
|
+
import path33 from "path";
|
|
15985
16394
|
import os4 from "os";
|
|
15986
16395
|
function defaultTierMigrationCycleBudget(config, trigger) {
|
|
15987
16396
|
if (trigger === "extraction") {
|
|
@@ -16028,7 +16437,7 @@ function sharedContextDir(config) {
|
|
|
16028
16437
|
if (typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0) {
|
|
16029
16438
|
return config.sharedContextDir;
|
|
16030
16439
|
}
|
|
16031
|
-
return
|
|
16440
|
+
return path33.join(os4.homedir(), ".openclaw", "workspace", "shared-context");
|
|
16032
16441
|
}
|
|
16033
16442
|
function cadenceStaleWindowMs(cadence) {
|
|
16034
16443
|
switch (cadence) {
|
|
@@ -16047,16 +16456,16 @@ function cadenceStaleWindowMs(cadence) {
|
|
|
16047
16456
|
var CompoundingEngine = class {
|
|
16048
16457
|
constructor(config) {
|
|
16049
16458
|
this.config = config;
|
|
16050
|
-
this.weeklyDir =
|
|
16051
|
-
this.rubricsPath =
|
|
16052
|
-
this.mistakesPath =
|
|
16053
|
-
this.feedbackInboxPath =
|
|
16054
|
-
this.identityAnchorPath =
|
|
16055
|
-
this.identityIncidentsDir =
|
|
16056
|
-
this.identityAuditWeeklyDir =
|
|
16057
|
-
this.identityAuditMonthlyDir =
|
|
16058
|
-
this.identityImprovementLoopsPath =
|
|
16059
|
-
this.memoryActionEventsPath =
|
|
16459
|
+
this.weeklyDir = path33.join(config.memoryDir, "compounding", "weekly");
|
|
16460
|
+
this.rubricsPath = path33.join(config.memoryDir, "compounding", "rubrics.md");
|
|
16461
|
+
this.mistakesPath = path33.join(config.memoryDir, "compounding", "mistakes.json");
|
|
16462
|
+
this.feedbackInboxPath = path33.join(sharedContextDir(config), "feedback", "inbox.jsonl");
|
|
16463
|
+
this.identityAnchorPath = path33.join(config.memoryDir, "identity", "identity-anchor.md");
|
|
16464
|
+
this.identityIncidentsDir = path33.join(config.memoryDir, "identity", "incidents");
|
|
16465
|
+
this.identityAuditWeeklyDir = path33.join(config.memoryDir, "identity", "audits", "weekly");
|
|
16466
|
+
this.identityAuditMonthlyDir = path33.join(config.memoryDir, "identity", "audits", "monthly");
|
|
16467
|
+
this.identityImprovementLoopsPath = path33.join(config.memoryDir, "identity", "improvement-loops.md");
|
|
16468
|
+
this.memoryActionEventsPath = path33.join(config.memoryDir, "state", "memory-actions.jsonl");
|
|
16060
16469
|
}
|
|
16061
16470
|
weeklyDir;
|
|
16062
16471
|
rubricsPath;
|
|
@@ -16069,9 +16478,9 @@ var CompoundingEngine = class {
|
|
|
16069
16478
|
identityImprovementLoopsPath;
|
|
16070
16479
|
memoryActionEventsPath;
|
|
16071
16480
|
async ensureDirs() {
|
|
16072
|
-
await
|
|
16073
|
-
await
|
|
16074
|
-
await
|
|
16481
|
+
await mkdir22(this.weeklyDir, { recursive: true });
|
|
16482
|
+
await mkdir22(path33.dirname(this.mistakesPath), { recursive: true });
|
|
16483
|
+
await mkdir22(path33.dirname(this.rubricsPath), { recursive: true });
|
|
16075
16484
|
}
|
|
16076
16485
|
async synthesizeWeekly(opts) {
|
|
16077
16486
|
await this.ensureDirs();
|
|
@@ -16082,12 +16491,12 @@ var CompoundingEngine = class {
|
|
|
16082
16491
|
const promotionCandidates = this.config.compoundingSemanticEnabled ? this.derivePromotionCandidates(outcomeSummary) : [];
|
|
16083
16492
|
const mistakes = this.buildMistakes(entries, actionPatterns);
|
|
16084
16493
|
const continuity = this.config.continuityAuditEnabled ? await this.readContinuityAuditReferences(weekId) : { monthId: monthIdFromIsoWeek(weekId), weeklyPath: null, monthlyPath: null };
|
|
16085
|
-
const reportPath =
|
|
16494
|
+
const reportPath = path33.join(this.weeklyDir, `${weekId}.md`);
|
|
16086
16495
|
const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns, mistakes.details, continuity, outcomeSummary, promotionCandidates);
|
|
16087
|
-
await
|
|
16496
|
+
await writeFile23(reportPath, md, "utf-8");
|
|
16088
16497
|
const rubrics = this.formatRubrics(entries, outcomeSummary);
|
|
16089
|
-
await
|
|
16090
|
-
await
|
|
16498
|
+
await writeFile23(this.rubricsPath, rubrics, "utf-8");
|
|
16499
|
+
await writeFile23(
|
|
16091
16500
|
this.mistakesPath,
|
|
16092
16501
|
JSON.stringify({ updatedAt: mistakes.updatedAt, patterns: mistakes.patterns }, null, 2) + "\n",
|
|
16093
16502
|
"utf-8"
|
|
@@ -16167,9 +16576,9 @@ var CompoundingEngine = class {
|
|
|
16167
16576
|
""
|
|
16168
16577
|
];
|
|
16169
16578
|
const dir = period === "weekly" ? this.identityAuditWeeklyDir : this.identityAuditMonthlyDir;
|
|
16170
|
-
await
|
|
16171
|
-
const reportPath =
|
|
16172
|
-
await
|
|
16579
|
+
await mkdir22(dir, { recursive: true });
|
|
16580
|
+
const reportPath = path33.join(dir, `${key}.md`);
|
|
16581
|
+
await writeFile23(reportPath, lines.join("\n"), "utf-8");
|
|
16173
16582
|
return { period, key, reportPath };
|
|
16174
16583
|
}
|
|
16175
16584
|
async readMistakes() {
|
|
@@ -16268,7 +16677,7 @@ var CompoundingEngine = class {
|
|
|
16268
16677
|
else if (event.outcome === "skipped") acc.counts.skipped += 1;
|
|
16269
16678
|
else if (event.outcome === "failed") acc.counts.failed += 1;
|
|
16270
16679
|
else acc.counts.unknown += 1;
|
|
16271
|
-
acc.provenance.add(`${
|
|
16680
|
+
acc.provenance.add(`${path33.basename(this.memoryActionEventsPath)}:L${event.line}`);
|
|
16272
16681
|
byAction.set(key, acc);
|
|
16273
16682
|
}
|
|
16274
16683
|
const out = [];
|
|
@@ -16306,7 +16715,7 @@ var CompoundingEngine = class {
|
|
|
16306
16715
|
const patterns = [];
|
|
16307
16716
|
for (const wrapped of entries) {
|
|
16308
16717
|
const e = wrapped.entry;
|
|
16309
|
-
const provenance = [`${
|
|
16718
|
+
const provenance = [`${path33.basename(wrapped.sourcePath)}:L${wrapped.sourceLine}#${wrapped.entryId}`];
|
|
16310
16719
|
if (e.learning && e.learning.trim().length > 0) {
|
|
16311
16720
|
patterns.push({ pattern: `${e.agent}: ${e.learning.trim()}`, provenance });
|
|
16312
16721
|
continue;
|
|
@@ -16316,7 +16725,7 @@ var CompoundingEngine = class {
|
|
|
16316
16725
|
}
|
|
16317
16726
|
}
|
|
16318
16727
|
for (const p of actionPatterns) {
|
|
16319
|
-
patterns.push({ pattern: p, provenance: [`${
|
|
16728
|
+
patterns.push({ pattern: p, provenance: [`${path33.basename(this.memoryActionEventsPath)}:*`] });
|
|
16320
16729
|
}
|
|
16321
16730
|
const byPattern = /* @__PURE__ */ new Map();
|
|
16322
16731
|
for (const p of patterns) {
|
|
@@ -16356,7 +16765,7 @@ var CompoundingEngine = class {
|
|
|
16356
16765
|
lines.push(`- approved: ${approved}`);
|
|
16357
16766
|
lines.push(`- approved_with_feedback: ${awf}`);
|
|
16358
16767
|
lines.push(`- rejected: ${rejected}`);
|
|
16359
|
-
const provenance = list.slice(0, 3).map((e) => `${
|
|
16768
|
+
const provenance = list.slice(0, 3).map((e) => `${path33.basename(e.sourcePath)}:L${e.sourceLine}#${e.entryId}`);
|
|
16360
16769
|
if (provenance.length > 0) {
|
|
16361
16770
|
lines.push(`- provenance: ${provenance.join(", ")}`);
|
|
16362
16771
|
}
|
|
@@ -16447,7 +16856,7 @@ var CompoundingEngine = class {
|
|
|
16447
16856
|
} else {
|
|
16448
16857
|
for (const item of learnings) {
|
|
16449
16858
|
const note = (item.entry.learning && item.entry.learning.trim().length > 0 ? item.entry.learning : item.entry.reason).trim();
|
|
16450
|
-
lines.push(`- ${note} _(source: ${
|
|
16859
|
+
lines.push(`- ${note} _(source: ${path33.basename(item.sourcePath)}:L${item.sourceLine}#${item.entryId})_`);
|
|
16451
16860
|
}
|
|
16452
16861
|
}
|
|
16453
16862
|
lines.push("");
|
|
@@ -16492,7 +16901,7 @@ var CompoundingEngine = class {
|
|
|
16492
16901
|
const files = names.filter((n) => n.endsWith(".md")).sort().reverse();
|
|
16493
16902
|
for (const file of files) {
|
|
16494
16903
|
if (incidents.length >= cappedLimit) break;
|
|
16495
|
-
const filePath =
|
|
16904
|
+
const filePath = path33.join(this.identityIncidentsDir, file);
|
|
16496
16905
|
try {
|
|
16497
16906
|
const raw = await readFile20(filePath, "utf-8");
|
|
16498
16907
|
const parsed = parseContinuityIncident(raw);
|
|
@@ -16508,8 +16917,8 @@ var CompoundingEngine = class {
|
|
|
16508
16917
|
}
|
|
16509
16918
|
async readContinuityAuditReferences(weekId) {
|
|
16510
16919
|
const monthId = monthIdFromIsoWeek(weekId);
|
|
16511
|
-
const weeklyPath =
|
|
16512
|
-
const monthlyPath =
|
|
16920
|
+
const weeklyPath = path33.join(this.identityAuditWeeklyDir, `${weekId}.md`);
|
|
16921
|
+
const monthlyPath = path33.join(this.identityAuditMonthlyDir, `${monthId}.md`);
|
|
16513
16922
|
const weeklyExists = await this.readNonEmptyFile(weeklyPath);
|
|
16514
16923
|
const monthlyExists = await this.readNonEmptyFile(monthlyPath);
|
|
16515
16924
|
return {
|
|
@@ -16522,8 +16931,8 @@ var CompoundingEngine = class {
|
|
|
16522
16931
|
};
|
|
16523
16932
|
|
|
16524
16933
|
// src/tier-migration.ts
|
|
16525
|
-
import { appendFile as appendFile5, mkdir as
|
|
16526
|
-
import
|
|
16934
|
+
import { appendFile as appendFile5, mkdir as mkdir23 } from "fs/promises";
|
|
16935
|
+
import path34 from "path";
|
|
16527
16936
|
var TierMigrationExecutor = class {
|
|
16528
16937
|
storage;
|
|
16529
16938
|
qmd;
|
|
@@ -16537,7 +16946,7 @@ var TierMigrationExecutor = class {
|
|
|
16537
16946
|
this.hotCollection = options.hotCollection;
|
|
16538
16947
|
this.coldCollection = options.coldCollection;
|
|
16539
16948
|
this.autoEmbed = options.autoEmbed === true;
|
|
16540
|
-
this.journalPath = options.journalPath ??
|
|
16949
|
+
this.journalPath = options.journalPath ?? path34.join(this.storage.dir, "state", "tier-migration-journal.jsonl");
|
|
16541
16950
|
}
|
|
16542
16951
|
async migrateMemory(request) {
|
|
16543
16952
|
const { memory, fromTier, toTier, reason } = request;
|
|
@@ -16590,7 +16999,7 @@ var TierMigrationExecutor = class {
|
|
|
16590
16999
|
reason: result.reason,
|
|
16591
17000
|
targetPath: result.targetPath
|
|
16592
17001
|
};
|
|
16593
|
-
await
|
|
17002
|
+
await mkdir23(path34.dirname(this.journalPath), { recursive: true });
|
|
16594
17003
|
await appendFile5(this.journalPath, `${JSON.stringify(entry)}
|
|
16595
17004
|
`, "utf-8");
|
|
16596
17005
|
}
|
|
@@ -16757,8 +17166,8 @@ function selectRouteRule(text, rules, options) {
|
|
|
16757
17166
|
}
|
|
16758
17167
|
|
|
16759
17168
|
// src/routing/store.ts
|
|
16760
|
-
import { lstat, mkdir as
|
|
16761
|
-
import
|
|
17169
|
+
import { lstat, mkdir as mkdir24, readFile as readFile21, realpath, rename as rename2, rm as rm3, stat as stat6, writeFile as writeFile24 } from "fs/promises";
|
|
17170
|
+
import path35 from "path";
|
|
16762
17171
|
import { createHash as createHash4 } from "crypto";
|
|
16763
17172
|
function defaultState() {
|
|
16764
17173
|
return {
|
|
@@ -16777,14 +17186,14 @@ function stableRuleId(rule) {
|
|
|
16777
17186
|
return `route-${createHash4("sha256").update(seed).digest("hex").slice(0, 12)}`;
|
|
16778
17187
|
}
|
|
16779
17188
|
function resolveStatePath(memoryDir, stateFile) {
|
|
16780
|
-
const root =
|
|
16781
|
-
const defaultPath =
|
|
16782
|
-
if (
|
|
16783
|
-
const absolute =
|
|
16784
|
-
return absolute.startsWith(root +
|
|
17189
|
+
const root = path35.resolve(memoryDir);
|
|
17190
|
+
const defaultPath = path35.join(root, "state", "routing-rules.json");
|
|
17191
|
+
if (path35.isAbsolute(stateFile)) {
|
|
17192
|
+
const absolute = path35.resolve(stateFile);
|
|
17193
|
+
return absolute.startsWith(root + path35.sep) ? absolute : defaultPath;
|
|
16785
17194
|
}
|
|
16786
|
-
const resolved =
|
|
16787
|
-
return resolved.startsWith(root +
|
|
17195
|
+
const resolved = path35.resolve(root, stateFile);
|
|
17196
|
+
return resolved.startsWith(root + path35.sep) ? resolved : defaultPath;
|
|
16788
17197
|
}
|
|
16789
17198
|
function normalizeRule(rule, options) {
|
|
16790
17199
|
if (!rule || typeof rule !== "object") return null;
|
|
@@ -16817,7 +17226,7 @@ var RoutingRulesStore = class {
|
|
|
16817
17226
|
lockPath;
|
|
16818
17227
|
writeQueue = Promise.resolve();
|
|
16819
17228
|
constructor(memoryDir, stateFile = "state/routing-rules.json") {
|
|
16820
|
-
this.memoryRoot =
|
|
17229
|
+
this.memoryRoot = path35.resolve(memoryDir);
|
|
16821
17230
|
this.statePath = resolveStatePath(memoryDir, stateFile);
|
|
16822
17231
|
this.lockPath = `${this.statePath}.lock`;
|
|
16823
17232
|
}
|
|
@@ -16855,7 +17264,7 @@ var RoutingRulesStore = class {
|
|
|
16855
17264
|
await this.withWriteLock(async () => {
|
|
16856
17265
|
const payload = defaultState();
|
|
16857
17266
|
await this.assertStatePathScoped();
|
|
16858
|
-
await
|
|
17267
|
+
await writeFile24(this.statePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
16859
17268
|
});
|
|
16860
17269
|
}
|
|
16861
17270
|
dedupeById(rules) {
|
|
@@ -16889,7 +17298,7 @@ var RoutingRulesStore = class {
|
|
|
16889
17298
|
const tmpPath = `${this.statePath}.tmp-${process.pid}-${Date.now()}`;
|
|
16890
17299
|
try {
|
|
16891
17300
|
await this.assertStatePathScoped();
|
|
16892
|
-
await
|
|
17301
|
+
await writeFile24(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
16893
17302
|
await rename2(tmpPath, this.statePath);
|
|
16894
17303
|
} catch (err) {
|
|
16895
17304
|
log.debug(`routing rules write failed: ${err}`);
|
|
@@ -16923,10 +17332,10 @@ var RoutingRulesStore = class {
|
|
|
16923
17332
|
const timeoutMs = 5e3;
|
|
16924
17333
|
let unexpectedLockError = null;
|
|
16925
17334
|
await this.assertStatePathScoped();
|
|
16926
|
-
await
|
|
17335
|
+
await mkdir24(path35.dirname(this.lockPath), { recursive: true });
|
|
16927
17336
|
while (Date.now() - start < timeoutMs) {
|
|
16928
17337
|
try {
|
|
16929
|
-
await
|
|
17338
|
+
await mkdir24(this.lockPath);
|
|
16930
17339
|
return async () => {
|
|
16931
17340
|
try {
|
|
16932
17341
|
await rm3(this.lockPath, { recursive: true, force: true });
|
|
@@ -16956,14 +17365,14 @@ var RoutingRulesStore = class {
|
|
|
16956
17365
|
throw new Error(`routing rules lock acquisition timed out after ${timeoutMs}ms`);
|
|
16957
17366
|
}
|
|
16958
17367
|
async assertStatePathScoped() {
|
|
16959
|
-
await
|
|
17368
|
+
await mkdir24(this.memoryRoot, { recursive: true });
|
|
16960
17369
|
const canonicalRoot = await realpath(this.memoryRoot);
|
|
16961
|
-
const canonicalParent = await this.canonicalizePathWithoutCreating(
|
|
16962
|
-
const canonicalStatePath =
|
|
17370
|
+
const canonicalParent = await this.canonicalizePathWithoutCreating(path35.dirname(this.statePath));
|
|
17371
|
+
const canonicalStatePath = path35.join(canonicalParent, path35.basename(this.statePath));
|
|
16963
17372
|
if (!this.isPathInside(canonicalRoot, canonicalStatePath)) {
|
|
16964
17373
|
throw new Error(`routing rules state path escaped memoryDir: ${canonicalStatePath}`);
|
|
16965
17374
|
}
|
|
16966
|
-
await
|
|
17375
|
+
await mkdir24(path35.dirname(this.statePath), { recursive: true });
|
|
16967
17376
|
try {
|
|
16968
17377
|
const stateStats = await lstat(this.statePath);
|
|
16969
17378
|
if (stateStats.isSymbolicLink()) {
|
|
@@ -16980,28 +17389,28 @@ var RoutingRulesStore = class {
|
|
|
16980
17389
|
}
|
|
16981
17390
|
}
|
|
16982
17391
|
isPathInside(root, candidate) {
|
|
16983
|
-
const normalizedRoot =
|
|
16984
|
-
const normalizedCandidate =
|
|
17392
|
+
const normalizedRoot = path35.resolve(root);
|
|
17393
|
+
const normalizedCandidate = path35.resolve(candidate);
|
|
16985
17394
|
if (normalizedCandidate === normalizedRoot) return true;
|
|
16986
|
-
if (normalizedRoot ===
|
|
17395
|
+
if (normalizedRoot === path35.parse(normalizedRoot).root) {
|
|
16987
17396
|
return normalizedCandidate.startsWith(normalizedRoot);
|
|
16988
17397
|
}
|
|
16989
|
-
return normalizedCandidate.startsWith(`${normalizedRoot}${
|
|
17398
|
+
return normalizedCandidate.startsWith(`${normalizedRoot}${path35.sep}`);
|
|
16990
17399
|
}
|
|
16991
17400
|
async canonicalizePathWithoutCreating(targetPath) {
|
|
16992
|
-
const absoluteTarget =
|
|
17401
|
+
const absoluteTarget = path35.resolve(targetPath);
|
|
16993
17402
|
let probe = absoluteTarget;
|
|
16994
17403
|
while (true) {
|
|
16995
17404
|
try {
|
|
16996
17405
|
const canonicalProbe = await realpath(probe);
|
|
16997
|
-
const remainder =
|
|
16998
|
-
return
|
|
17406
|
+
const remainder = path35.relative(probe, absoluteTarget);
|
|
17407
|
+
return path35.resolve(canonicalProbe, remainder);
|
|
16999
17408
|
} catch (err) {
|
|
17000
17409
|
const code = err.code;
|
|
17001
17410
|
if (code !== "ENOENT") {
|
|
17002
17411
|
throw err;
|
|
17003
17412
|
}
|
|
17004
|
-
const parent =
|
|
17413
|
+
const parent = path35.dirname(probe);
|
|
17005
17414
|
if (parent === probe) {
|
|
17006
17415
|
return absoluteTarget;
|
|
17007
17416
|
}
|
|
@@ -17012,8 +17421,8 @@ var RoutingRulesStore = class {
|
|
|
17012
17421
|
};
|
|
17013
17422
|
|
|
17014
17423
|
// src/policy-runtime.ts
|
|
17015
|
-
import
|
|
17016
|
-
import { mkdir as
|
|
17424
|
+
import path36 from "path";
|
|
17425
|
+
import { mkdir as mkdir25, readFile as readFile22, rename as rename3, writeFile as writeFile25 } from "fs/promises";
|
|
17017
17426
|
var RUNTIME_POLICY_VERSION = 1;
|
|
17018
17427
|
var RUNTIME_POLICY_FILE = "policy-runtime.json";
|
|
17019
17428
|
var RUNTIME_POLICY_PREV_FILE = "policy-runtime.prev.json";
|
|
@@ -17061,8 +17470,8 @@ async function readRuntimePolicySnapshot(filePath, options) {
|
|
|
17061
17470
|
}
|
|
17062
17471
|
async function writeSnapshotAtomic(filePath, snapshot) {
|
|
17063
17472
|
const tempPath = `${filePath}.tmp`;
|
|
17064
|
-
await
|
|
17065
|
-
await
|
|
17473
|
+
await mkdir25(path36.dirname(filePath), { recursive: true });
|
|
17474
|
+
await writeFile25(tempPath, `${JSON.stringify(snapshot, null, 2)}
|
|
17066
17475
|
`, "utf-8");
|
|
17067
17476
|
await rename3(tempPath, filePath);
|
|
17068
17477
|
}
|
|
@@ -17070,9 +17479,9 @@ var PolicyRuntimeManager = class {
|
|
|
17070
17479
|
constructor(memoryDir, config) {
|
|
17071
17480
|
this.memoryDir = memoryDir;
|
|
17072
17481
|
this.config = config;
|
|
17073
|
-
const stateDir2 =
|
|
17074
|
-
this.runtimePath =
|
|
17075
|
-
this.runtimePrevPath =
|
|
17482
|
+
const stateDir2 = path36.join(memoryDir, "state");
|
|
17483
|
+
this.runtimePath = path36.join(stateDir2, RUNTIME_POLICY_FILE);
|
|
17484
|
+
this.runtimePrevPath = path36.join(stateDir2, RUNTIME_POLICY_PREV_FILE);
|
|
17076
17485
|
}
|
|
17077
17486
|
runtimePath;
|
|
17078
17487
|
runtimePrevPath;
|
|
@@ -17225,7 +17634,7 @@ function dedupeBehaviorSignalsByMemoryAndHash(signals) {
|
|
|
17225
17634
|
// src/orchestrator.ts
|
|
17226
17635
|
var COMPACTION_SIGNAL_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
17227
17636
|
function defaultWorkspaceDir() {
|
|
17228
|
-
return
|
|
17637
|
+
return path37.join(os5.homedir(), ".openclaw", "workspace");
|
|
17229
17638
|
}
|
|
17230
17639
|
function sanitizeSessionKeyForFilename(sessionKey) {
|
|
17231
17640
|
const readable = sessionKey.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
@@ -17365,11 +17774,11 @@ function mergeGraphExpandedResults(primary, expanded) {
|
|
|
17365
17774
|
return Array.from(mergedByPath.values());
|
|
17366
17775
|
}
|
|
17367
17776
|
function graphPathRelativeToStorage(storageDir, candidatePath) {
|
|
17368
|
-
const absolutePath =
|
|
17369
|
-
const rel =
|
|
17777
|
+
const absolutePath = path37.isAbsolute(candidatePath) ? candidatePath : path37.resolve(storageDir, candidatePath);
|
|
17778
|
+
const rel = path37.relative(storageDir, absolutePath);
|
|
17370
17779
|
if (!rel || rel === ".") return null;
|
|
17371
17780
|
if (rel.startsWith("..")) return null;
|
|
17372
|
-
return rel.split(
|
|
17781
|
+
return rel.split(path37.sep).join("/");
|
|
17373
17782
|
}
|
|
17374
17783
|
function normalizeGraphActivationScore(score) {
|
|
17375
17784
|
const bounded = Number.isFinite(score) && score > 0 ? score : 0;
|
|
@@ -17444,7 +17853,7 @@ function buildMemoryPathById(allMemsForGraph, storageDir) {
|
|
|
17444
17853
|
for (const mem of allMemsForGraph ?? []) {
|
|
17445
17854
|
const id = mem.frontmatter.id;
|
|
17446
17855
|
if (!id) continue;
|
|
17447
|
-
pathById.set(id,
|
|
17856
|
+
pathById.set(id, path37.relative(storageDir, mem.path));
|
|
17448
17857
|
}
|
|
17449
17858
|
return pathById;
|
|
17450
17859
|
}
|
|
@@ -17452,7 +17861,7 @@ function appendMemoryToGraphContext(options) {
|
|
|
17452
17861
|
if (!Array.isArray(options.allMemsForGraph)) return;
|
|
17453
17862
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
17454
17863
|
options.allMemsForGraph.push({
|
|
17455
|
-
path:
|
|
17864
|
+
path: path37.join(options.storageDir, options.memoryRelPath),
|
|
17456
17865
|
content: options.content,
|
|
17457
17866
|
frontmatter: {
|
|
17458
17867
|
id: options.memoryId,
|
|
@@ -17472,15 +17881,15 @@ function resolvePersistedMemoryRelativePath(options) {
|
|
|
17472
17881
|
const persisted = options.pathById.get(options.memoryId);
|
|
17473
17882
|
if (persisted) return persisted;
|
|
17474
17883
|
if (options.category === "correction") {
|
|
17475
|
-
return
|
|
17884
|
+
return path37.join("corrections", `${options.memoryId}.md`);
|
|
17476
17885
|
}
|
|
17477
17886
|
const idParts = options.memoryId.split("-");
|
|
17478
17887
|
const maybeTimestamp = Number(idParts[1]);
|
|
17479
17888
|
if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
|
|
17480
17889
|
const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
|
|
17481
|
-
return
|
|
17890
|
+
return path37.join("facts", day, `${options.memoryId}.md`);
|
|
17482
17891
|
}
|
|
17483
|
-
return
|
|
17892
|
+
return path37.join("facts", `${options.memoryId}.md`);
|
|
17484
17893
|
}
|
|
17485
17894
|
function shouldRejectLowConfidenceRecall(results, threshold) {
|
|
17486
17895
|
if (results.length === 0) return false;
|
|
@@ -17588,7 +17997,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17588
17997
|
this.compounding = config.compoundingEnabled ? new CompoundingEngine(config) : void 0;
|
|
17589
17998
|
this.buffer = new SmartBuffer(config, this.storage);
|
|
17590
17999
|
this.transcript = new TranscriptManager(config);
|
|
17591
|
-
this.conversationIndexDir =
|
|
18000
|
+
this.conversationIndexDir = path37.join(config.memoryDir, "conversation-index", "chunks");
|
|
17592
18001
|
this.modelRegistry = new ModelRegistry(config.memoryDir);
|
|
17593
18002
|
this.relevance = new RelevanceStore(config.memoryDir);
|
|
17594
18003
|
this.negatives = new NegativeExampleStore(config.memoryDir);
|
|
@@ -17613,7 +18022,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17613
18022
|
})() : this.localLlm;
|
|
17614
18023
|
this.extraction = new ExtractionEngine(config, this.localLlm, config.gatewayConfig, this.modelRegistry);
|
|
17615
18024
|
this.threading = new ThreadingManager(
|
|
17616
|
-
|
|
18025
|
+
path37.join(config.memoryDir, "threads"),
|
|
17617
18026
|
config.threadingGapMinutes
|
|
17618
18027
|
);
|
|
17619
18028
|
this.tmtBuilder = new TmtBuilder(config.memoryDir, {
|
|
@@ -17782,7 +18191,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17782
18191
|
await this.sessionObserver.load();
|
|
17783
18192
|
this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
|
|
17784
18193
|
if (this.config.factDeduplicationEnabled) {
|
|
17785
|
-
const stateDir2 =
|
|
18194
|
+
const stateDir2 = path37.join(this.config.memoryDir, "state");
|
|
17786
18195
|
this.contentHashIndex = new ContentHashIndex(stateDir2);
|
|
17787
18196
|
await this.contentHashIndex.load();
|
|
17788
18197
|
log.info(`content-hash dedup: loaded ${this.contentHashIndex.size} hashes`);
|
|
@@ -17821,7 +18230,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17821
18230
|
if (available) {
|
|
17822
18231
|
log.info(`Conversation index QMD: available ${this.conversationQmd.debugStatus()}`);
|
|
17823
18232
|
const collectionState = await this.conversationQmd.ensureCollection(
|
|
17824
|
-
|
|
18233
|
+
path37.join(this.config.memoryDir, "conversation-index")
|
|
17825
18234
|
);
|
|
17826
18235
|
if (collectionState === "missing") {
|
|
17827
18236
|
this.config.conversationIndexEnabled = false;
|
|
@@ -17857,7 +18266,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17857
18266
|
const files = await readdir16(wsDir).catch(() => []);
|
|
17858
18267
|
for (const f of files) {
|
|
17859
18268
|
if (!f.startsWith(".compaction-reset-signal-")) continue;
|
|
17860
|
-
const fp =
|
|
18269
|
+
const fp = path37.join(wsDir, f);
|
|
17861
18270
|
const s = await stat7(fp).catch(() => null);
|
|
17862
18271
|
if (s && Date.now() - s.mtimeMs >= COMPACTION_SIGNAL_MAX_AGE_MS) {
|
|
17863
18272
|
await unlink5(fp).catch(() => {
|
|
@@ -17893,12 +18302,12 @@ var Orchestrator = class _Orchestrator {
|
|
|
17893
18302
|
this.lastFileHygieneRunAtMs = now;
|
|
17894
18303
|
if (hygiene.rotateEnabled) {
|
|
17895
18304
|
for (const rel of hygiene.rotatePaths) {
|
|
17896
|
-
const abs =
|
|
18305
|
+
const abs = path37.isAbsolute(rel) ? rel : path37.join(this.config.workspaceDir, rel);
|
|
17897
18306
|
try {
|
|
17898
18307
|
const raw = await readFile23(abs, "utf-8");
|
|
17899
18308
|
if (raw.length > hygiene.rotateMaxBytes) {
|
|
17900
|
-
const archiveDir =
|
|
17901
|
-
const base =
|
|
18309
|
+
const archiveDir = path37.join(this.config.workspaceDir, hygiene.archiveDir);
|
|
18310
|
+
const base = path37.basename(abs);
|
|
17902
18311
|
const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
|
|
17903
18312
|
const { newContent } = await rotateMarkdownFileToArchive({
|
|
17904
18313
|
filePath: abs,
|
|
@@ -17906,7 +18315,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17906
18315
|
archivePrefix: prefix,
|
|
17907
18316
|
keepTailChars: hygiene.rotateKeepTailChars
|
|
17908
18317
|
});
|
|
17909
|
-
await
|
|
18318
|
+
await writeFile26(abs, newContent, "utf-8");
|
|
17910
18319
|
}
|
|
17911
18320
|
} catch {
|
|
17912
18321
|
}
|
|
@@ -17923,8 +18332,8 @@ var Orchestrator = class _Orchestrator {
|
|
|
17923
18332
|
log.warn(w.message);
|
|
17924
18333
|
}
|
|
17925
18334
|
if (hygiene.warningsLogEnabled && warnings.length > 0) {
|
|
17926
|
-
const fp =
|
|
17927
|
-
await
|
|
18335
|
+
const fp = path37.join(this.config.memoryDir, hygiene.warningsLogPath);
|
|
18336
|
+
await mkdir26(path37.dirname(fp), { recursive: true });
|
|
17928
18337
|
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
17929
18338
|
const block = `
|
|
17930
18339
|
|
|
@@ -17937,7 +18346,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17937
18346
|
} catch {
|
|
17938
18347
|
existing = "# Engram File Hygiene Warnings\n";
|
|
17939
18348
|
}
|
|
17940
|
-
await
|
|
18349
|
+
await writeFile26(fp, existing + block, "utf-8");
|
|
17941
18350
|
}
|
|
17942
18351
|
}
|
|
17943
18352
|
}
|
|
@@ -18013,7 +18422,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
18013
18422
|
}
|
|
18014
18423
|
async getLastGraphRecallSnapshot(namespace) {
|
|
18015
18424
|
const storage = await this.getStorage(namespace);
|
|
18016
|
-
const snapshotPath =
|
|
18425
|
+
const snapshotPath = path37.join(storage.dir, "state", "last_graph_recall.json");
|
|
18017
18426
|
try {
|
|
18018
18427
|
const raw = await readFile23(snapshotPath, "utf-8");
|
|
18019
18428
|
const parsed = JSON.parse(raw);
|
|
@@ -18084,7 +18493,7 @@ ${r.snippet.trim()}
|
|
|
18084
18493
|
const entries = await readdir16(dir, { withFileTypes: true });
|
|
18085
18494
|
let total = 0;
|
|
18086
18495
|
for (const entry of entries) {
|
|
18087
|
-
const fullPath =
|
|
18496
|
+
const fullPath = path37.join(dir, entry.name);
|
|
18088
18497
|
if (entry.isDirectory()) {
|
|
18089
18498
|
total += await this.countConversationChunkDocs(fullPath);
|
|
18090
18499
|
continue;
|
|
@@ -18409,7 +18818,7 @@ ${r.snippet.trim()}
|
|
|
18409
18818
|
const seedRelativePaths = seedCandidates.map((result) => graphPathRelativeToStorage(storage.dir, result.path)).filter((value) => typeof value === "string" && value.length > 0);
|
|
18410
18819
|
if (seedRelativePaths.length === 0) continue;
|
|
18411
18820
|
const seedRecallScore = seedCandidates.reduce((max, item) => Math.max(max, item.score), 0);
|
|
18412
|
-
seedPaths.push(...seedRelativePaths.map((rel) =>
|
|
18821
|
+
seedPaths.push(...seedRelativePaths.map((rel) => path37.join(storage.dir, rel)));
|
|
18413
18822
|
const seedSet = new Set(seedRelativePaths);
|
|
18414
18823
|
const expanded = await this.graphIndexFor(storage).spreadingActivation(
|
|
18415
18824
|
seedRelativePaths,
|
|
@@ -18418,7 +18827,7 @@ ${r.snippet.trim()}
|
|
|
18418
18827
|
if (expanded.length === 0) continue;
|
|
18419
18828
|
for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
|
|
18420
18829
|
if (seedSet.has(candidate.path)) continue;
|
|
18421
|
-
const memoryPath =
|
|
18830
|
+
const memoryPath = path37.resolve(storage.dir, candidate.path);
|
|
18422
18831
|
const memory = await storage.readMemoryByPath(memoryPath);
|
|
18423
18832
|
if (!memory) continue;
|
|
18424
18833
|
if (isArtifactMemoryPath(memory.path)) continue;
|
|
@@ -18441,7 +18850,7 @@ ${r.snippet.trim()}
|
|
|
18441
18850
|
path: memory.path,
|
|
18442
18851
|
score,
|
|
18443
18852
|
namespace,
|
|
18444
|
-
seed:
|
|
18853
|
+
seed: path37.resolve(storage.dir, candidate.seed),
|
|
18445
18854
|
hopDepth: candidate.hopDepth,
|
|
18446
18855
|
decayedWeight: candidate.decayedWeight,
|
|
18447
18856
|
graphType: candidate.graphType
|
|
@@ -18456,8 +18865,8 @@ ${r.snippet.trim()}
|
|
|
18456
18865
|
}
|
|
18457
18866
|
async recordLastGraphRecallSnapshot(options) {
|
|
18458
18867
|
try {
|
|
18459
|
-
const snapshotPath =
|
|
18460
|
-
await
|
|
18868
|
+
const snapshotPath = path37.join(options.storage.dir, "state", "last_graph_recall.json");
|
|
18869
|
+
await mkdir26(path37.dirname(snapshotPath), { recursive: true });
|
|
18461
18870
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18462
18871
|
const totalSeedCount = options.seedPaths.length;
|
|
18463
18872
|
const totalExpandedCount = options.expandedPaths.length;
|
|
@@ -18474,7 +18883,7 @@ ${r.snippet.trim()}
|
|
|
18474
18883
|
seeds,
|
|
18475
18884
|
expanded
|
|
18476
18885
|
};
|
|
18477
|
-
await
|
|
18886
|
+
await writeFile26(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
18478
18887
|
} catch (err) {
|
|
18479
18888
|
log.debug(`last graph recall write failed: ${err}`);
|
|
18480
18889
|
}
|
|
@@ -18783,7 +19192,49 @@ ${r.snippet.trim()}
|
|
|
18783
19192
|
timings.trustZones = `${Date.now() - t0}ms`;
|
|
18784
19193
|
return results.length > 0 ? this.formatTrustZoneResults(results) : null;
|
|
18785
19194
|
})();
|
|
18786
|
-
const
|
|
19195
|
+
const harmonicRetrievalPromise = (async () => {
|
|
19196
|
+
const t0 = Date.now();
|
|
19197
|
+
if (!this.config.harmonicRetrievalEnabled || !this.isRecallSectionEnabled("harmonic-retrieval", this.config.harmonicRetrievalEnabled === true)) {
|
|
19198
|
+
timings.harmonicRetrieval = "skip";
|
|
19199
|
+
return null;
|
|
19200
|
+
}
|
|
19201
|
+
const maxResults = this.getRecallSectionNumber("harmonic-retrieval", "maxResults") ?? 3;
|
|
19202
|
+
if (maxResults <= 0) {
|
|
19203
|
+
timings.harmonicRetrieval = "skip(limit=0)";
|
|
19204
|
+
return null;
|
|
19205
|
+
}
|
|
19206
|
+
const results = await searchHarmonicRetrieval({
|
|
19207
|
+
memoryDir: this.config.memoryDir,
|
|
19208
|
+
abstractionNodeStoreDir: this.config.abstractionNodeStoreDir,
|
|
19209
|
+
query: retrievalQuery,
|
|
19210
|
+
maxResults,
|
|
19211
|
+
sessionKey,
|
|
19212
|
+
anchorsEnabled: this.config.abstractionAnchorsEnabled
|
|
19213
|
+
});
|
|
19214
|
+
timings.harmonicRetrieval = `${Date.now() - t0}ms`;
|
|
19215
|
+
return results.length > 0 ? this.formatHarmonicRetrievalResults(results) : null;
|
|
19216
|
+
})();
|
|
19217
|
+
const verifiedRecallPromise = (async () => {
|
|
19218
|
+
const t0 = Date.now();
|
|
19219
|
+
if (!this.config.verifiedRecallEnabled || !this.isRecallSectionEnabled("verified-episodes", this.config.verifiedRecallEnabled === true)) {
|
|
19220
|
+
timings.verifiedRecall = "skip";
|
|
19221
|
+
return null;
|
|
19222
|
+
}
|
|
19223
|
+
const maxResults = this.getRecallSectionNumber("verified-episodes", "maxResults") ?? 3;
|
|
19224
|
+
if (maxResults <= 0) {
|
|
19225
|
+
timings.verifiedRecall = "skip(limit=0)";
|
|
19226
|
+
return null;
|
|
19227
|
+
}
|
|
19228
|
+
const results = await searchVerifiedEpisodes({
|
|
19229
|
+
memoryDir: this.config.memoryDir,
|
|
19230
|
+
query: retrievalQuery,
|
|
19231
|
+
maxResults,
|
|
19232
|
+
boxRecallDays: this.config.boxRecallDays
|
|
19233
|
+
});
|
|
19234
|
+
timings.verifiedRecall = `${Date.now() - t0}ms`;
|
|
19235
|
+
return results.length > 0 ? this.formatVerifiedEpisodeResults(results) : null;
|
|
19236
|
+
})();
|
|
19237
|
+
const qmdPromise = (async () => {
|
|
18787
19238
|
if (recallResultLimit <= 0) {
|
|
18788
19239
|
timings.qmd = "skip(limit=0)";
|
|
18789
19240
|
return null;
|
|
@@ -18856,8 +19307,8 @@ ${formatted}`;
|
|
|
18856
19307
|
if (!this.config.compactionResetEnabled) return null;
|
|
18857
19308
|
const workspaceDir = compactionWorkspaceDir || this.config.workspaceDir || defaultWorkspaceDir();
|
|
18858
19309
|
const safeSessionKey = sanitizeSessionKeyForFilename(effectiveSessionKey);
|
|
18859
|
-
const signalPath =
|
|
18860
|
-
const bootPath =
|
|
19310
|
+
const signalPath = path37.join(workspaceDir, `.compaction-reset-signal-${safeSessionKey}`);
|
|
19311
|
+
const bootPath = path37.join(workspaceDir, "BOOT.md");
|
|
18861
19312
|
try {
|
|
18862
19313
|
const signalStat = await stat7(signalPath).catch(() => null);
|
|
18863
19314
|
if (!signalStat) return null;
|
|
@@ -18989,6 +19440,8 @@ ${formatted}`;
|
|
|
18989
19440
|
objectiveStateSection,
|
|
18990
19441
|
causalTrajectorySection,
|
|
18991
19442
|
trustZoneSection,
|
|
19443
|
+
harmonicRetrievalSection,
|
|
19444
|
+
verifiedRecallSection,
|
|
18992
19445
|
qmdResult,
|
|
18993
19446
|
transcriptSection,
|
|
18994
19447
|
compactionSection,
|
|
@@ -19004,6 +19457,8 @@ ${formatted}`;
|
|
|
19004
19457
|
objectiveStatePromise,
|
|
19005
19458
|
causalTrajectoryPromise,
|
|
19006
19459
|
trustZonePromise,
|
|
19460
|
+
harmonicRetrievalPromise,
|
|
19461
|
+
verifiedRecallPromise,
|
|
19007
19462
|
qmdPromise,
|
|
19008
19463
|
transcriptPromise,
|
|
19009
19464
|
compactionPromise,
|
|
@@ -19067,6 +19522,12 @@ ${tmtNode.summary}`);
|
|
|
19067
19522
|
if (trustZoneSection) {
|
|
19068
19523
|
this.appendRecallSection(sectionBuckets, "trust-zones", trustZoneSection);
|
|
19069
19524
|
}
|
|
19525
|
+
if (harmonicRetrievalSection) {
|
|
19526
|
+
this.appendRecallSection(sectionBuckets, "harmonic-retrieval", harmonicRetrievalSection);
|
|
19527
|
+
}
|
|
19528
|
+
if (verifiedRecallSection) {
|
|
19529
|
+
this.appendRecallSection(sectionBuckets, "verified-episodes", verifiedRecallSection);
|
|
19530
|
+
}
|
|
19070
19531
|
if (qmdResult) {
|
|
19071
19532
|
const t0 = Date.now();
|
|
19072
19533
|
const { memoryResultsLists, globalResults } = qmdResult;
|
|
@@ -19860,7 +20321,7 @@ _Context: ${topQuestion.context}_`
|
|
|
19860
20321
|
};
|
|
19861
20322
|
this.tierMigrationInFlight = true;
|
|
19862
20323
|
try {
|
|
19863
|
-
const coldStorage = new StorageManager(
|
|
20324
|
+
const coldStorage = new StorageManager(path37.join(storage.dir, "cold"));
|
|
19864
20325
|
const [hotMemories, coldMemories] = await Promise.all([
|
|
19865
20326
|
storage.readAllMemories(),
|
|
19866
20327
|
coldStorage.readAllMemories()
|
|
@@ -20454,7 +20915,7 @@ _Context: ${topQuestion.context}_`
|
|
|
20454
20915
|
const allMems = allMemsForGraph ?? [];
|
|
20455
20916
|
for (const m of allMems) {
|
|
20456
20917
|
if (m.frontmatter.entityRef === entityRef) {
|
|
20457
|
-
const rel =
|
|
20918
|
+
const rel = path37.relative(storage.dir, m.path);
|
|
20458
20919
|
if (rel !== memoryRelPath) entitySiblings.push(rel);
|
|
20459
20920
|
}
|
|
20460
20921
|
}
|
|
@@ -21018,9 +21479,9 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
|
|
|
21018
21479
|
protectedCategories: this.config.lifecycleProtectedCategories
|
|
21019
21480
|
}
|
|
21020
21481
|
};
|
|
21021
|
-
const metricsPath =
|
|
21022
|
-
await
|
|
21023
|
-
await
|
|
21482
|
+
const metricsPath = path37.join(this.storage.dir, "state", "lifecycle-metrics.json");
|
|
21483
|
+
await mkdir26(path37.dirname(metricsPath), { recursive: true });
|
|
21484
|
+
await writeFile26(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
|
|
21024
21485
|
}
|
|
21025
21486
|
/**
|
|
21026
21487
|
* Archive old, low-importance, rarely-accessed facts (v6.0).
|
|
@@ -21242,6 +21703,54 @@ ${details.join("\n")}`;
|
|
|
21242
21703
|
});
|
|
21243
21704
|
return `## Trust Zones
|
|
21244
21705
|
|
|
21706
|
+
${lines.join("\n\n")}`;
|
|
21707
|
+
}
|
|
21708
|
+
formatHarmonicRetrievalResults(results) {
|
|
21709
|
+
const lines = results.map(({ node, matchedAnchors, matchedFields, nodeScore, anchorScore }, index) => {
|
|
21710
|
+
const header = [
|
|
21711
|
+
`[${index + 1}] ${node.recordedAt.replace("T", " ").slice(0, 16)}`,
|
|
21712
|
+
`${node.kind}/${node.abstractionLevel}`,
|
|
21713
|
+
node.sessionKey
|
|
21714
|
+
].join(" | ");
|
|
21715
|
+
const details = [
|
|
21716
|
+
node.title,
|
|
21717
|
+
node.summary,
|
|
21718
|
+
`scores: node=${nodeScore.toFixed(1)} anchor=${anchorScore.toFixed(1)}`
|
|
21719
|
+
];
|
|
21720
|
+
if (matchedAnchors.length > 0) {
|
|
21721
|
+
details.push(`anchors: ${matchedAnchors.map((anchor) => `${anchor.anchorType}:${anchor.anchorValue}`).join("; ")}`);
|
|
21722
|
+
}
|
|
21723
|
+
if (matchedFields.length > 0) {
|
|
21724
|
+
details.push(`matched: ${matchedFields.join(", ")}`);
|
|
21725
|
+
}
|
|
21726
|
+
return `${header}
|
|
21727
|
+
${details.join("\n")}`;
|
|
21728
|
+
});
|
|
21729
|
+
return `## Harmonic Retrieval
|
|
21730
|
+
|
|
21731
|
+
${lines.join("\n\n")}`;
|
|
21732
|
+
}
|
|
21733
|
+
formatVerifiedEpisodeResults(results) {
|
|
21734
|
+
const lines = results.map(({ box, verifiedEpisodeCount, matchedFields }, index) => {
|
|
21735
|
+
const header = [
|
|
21736
|
+
`[${index + 1}] ${box.sealedAt.replace("T", " ").slice(0, 16)}`,
|
|
21737
|
+
box.traceId ? `trace:${box.traceId.slice(0, 12)}` : "trace:none"
|
|
21738
|
+
].join(" | ");
|
|
21739
|
+
const details = [
|
|
21740
|
+
box.goal ?? `topics: ${box.topics.join(", ")}`,
|
|
21741
|
+
`verified episodes: ${verifiedEpisodeCount}`
|
|
21742
|
+
];
|
|
21743
|
+
if (box.toolsUsed && box.toolsUsed.length > 0) {
|
|
21744
|
+
details.push(`tools: ${box.toolsUsed.join(", ")}`);
|
|
21745
|
+
}
|
|
21746
|
+
if (matchedFields.length > 0) {
|
|
21747
|
+
details.push(`matched: ${matchedFields.join(", ")}`);
|
|
21748
|
+
}
|
|
21749
|
+
return `${header}
|
|
21750
|
+
${details.join("\n")}`;
|
|
21751
|
+
});
|
|
21752
|
+
return `## Verified Episodes
|
|
21753
|
+
|
|
21245
21754
|
${lines.join("\n\n")}`;
|
|
21246
21755
|
}
|
|
21247
21756
|
summarizeIdentityText(raw, maxLines, maxChars) {
|
|
@@ -21375,7 +21884,7 @@ ${lines.join("\n\n")}`;
|
|
|
21375
21884
|
if (hits.length === 0) return [];
|
|
21376
21885
|
const results = [];
|
|
21377
21886
|
for (const hit of hits) {
|
|
21378
|
-
const fullPath =
|
|
21887
|
+
const fullPath = path37.isAbsolute(hit.path) ? hit.path : path37.join(this.config.memoryDir, hit.path);
|
|
21379
21888
|
const memory = await this.storage.readMemoryByPath(fullPath);
|
|
21380
21889
|
if (!memory) continue;
|
|
21381
21890
|
results.push({
|
|
@@ -21833,8 +22342,8 @@ ${lines.join("\n\n")}`;
|
|
|
21833
22342
|
}
|
|
21834
22343
|
namespaceFromStorageDir(storageDir) {
|
|
21835
22344
|
if (!this.config.namespacesEnabled) return this.config.defaultNamespace;
|
|
21836
|
-
const resolvedStorageDir =
|
|
21837
|
-
const resolvedMemoryDir =
|
|
22345
|
+
const resolvedStorageDir = path37.resolve(storageDir);
|
|
22346
|
+
const resolvedMemoryDir = path37.resolve(this.config.memoryDir);
|
|
21838
22347
|
if (resolvedStorageDir === resolvedMemoryDir) return this.config.defaultNamespace;
|
|
21839
22348
|
const m = resolvedStorageDir.match(/[\\/]namespaces[\\/]([^\\/]+)$/);
|
|
21840
22349
|
return m && m[1] ? m[1] : this.config.defaultNamespace;
|
|
@@ -21862,14 +22371,14 @@ ${lines.join("\n\n")}`;
|
|
|
21862
22371
|
};
|
|
21863
22372
|
|
|
21864
22373
|
// src/tools.ts
|
|
21865
|
-
import
|
|
22374
|
+
import path39 from "path";
|
|
21866
22375
|
import { createHash as createHash7 } from "crypto";
|
|
21867
22376
|
import { Type } from "@sinclair/typebox";
|
|
21868
22377
|
|
|
21869
22378
|
// src/work/storage.ts
|
|
21870
|
-
import
|
|
22379
|
+
import path38 from "path";
|
|
21871
22380
|
import { randomUUID } from "crypto";
|
|
21872
|
-
import { mkdir as
|
|
22381
|
+
import { mkdir as mkdir27, readdir as readdir17, readFile as readFile24, rm as rm4, writeFile as writeFile27 } from "fs/promises";
|
|
21873
22382
|
var TASK_TRANSITIONS = {
|
|
21874
22383
|
todo: /* @__PURE__ */ new Set(["in_progress", "blocked", "cancelled"]),
|
|
21875
22384
|
in_progress: /* @__PURE__ */ new Set(["todo", "blocked", "done", "cancelled"]),
|
|
@@ -21944,22 +22453,22 @@ function ensureProjectStatus(value) {
|
|
|
21944
22453
|
var WorkStorage = class {
|
|
21945
22454
|
constructor(memoryDir) {
|
|
21946
22455
|
this.memoryDir = memoryDir;
|
|
21947
|
-
this.tasksDir =
|
|
21948
|
-
this.projectsDir =
|
|
22456
|
+
this.tasksDir = path38.join(memoryDir, "work", "tasks");
|
|
22457
|
+
this.projectsDir = path38.join(memoryDir, "work", "projects");
|
|
21949
22458
|
}
|
|
21950
22459
|
tasksDir;
|
|
21951
22460
|
projectsDir;
|
|
21952
22461
|
async ensureDirectories() {
|
|
21953
|
-
await
|
|
21954
|
-
await
|
|
22462
|
+
await mkdir27(this.tasksDir, { recursive: true });
|
|
22463
|
+
await mkdir27(this.projectsDir, { recursive: true });
|
|
21955
22464
|
}
|
|
21956
22465
|
taskPath(id) {
|
|
21957
22466
|
assertValidWorkId(id, "task");
|
|
21958
|
-
return
|
|
22467
|
+
return path38.join(this.tasksDir, `${id}.md`);
|
|
21959
22468
|
}
|
|
21960
22469
|
projectPath(id) {
|
|
21961
22470
|
assertValidWorkId(id, "project");
|
|
21962
|
-
return
|
|
22471
|
+
return path38.join(this.projectsDir, `${id}.md`);
|
|
21963
22472
|
}
|
|
21964
22473
|
serializeTask(task) {
|
|
21965
22474
|
return `${serializeFrontmatter2(task)}
|
|
@@ -22031,7 +22540,7 @@ ${project.description}
|
|
|
22031
22540
|
throw new Error(`project not found: ${task.projectId}`);
|
|
22032
22541
|
}
|
|
22033
22542
|
}
|
|
22034
|
-
await
|
|
22543
|
+
await writeFile27(this.taskPath(task.id), this.serializeTask(task), "utf-8");
|
|
22035
22544
|
if (task.projectId) {
|
|
22036
22545
|
await this.addTaskIdToProject(task.projectId, task.id, now);
|
|
22037
22546
|
}
|
|
@@ -22051,7 +22560,7 @@ ${project.description}
|
|
|
22051
22560
|
const out = [];
|
|
22052
22561
|
for (const entry of entries) {
|
|
22053
22562
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
22054
|
-
const raw = await readFile24(
|
|
22563
|
+
const raw = await readFile24(path38.join(this.tasksDir, entry.name), "utf-8");
|
|
22055
22564
|
const task = this.parseTask(raw);
|
|
22056
22565
|
if (!task) continue;
|
|
22057
22566
|
if (filter?.status && task.status !== filter.status) continue;
|
|
@@ -22093,7 +22602,7 @@ ${project.description}
|
|
|
22093
22602
|
tags: patch.tags ?? existing.tags,
|
|
22094
22603
|
updatedAt: now.toISOString()
|
|
22095
22604
|
};
|
|
22096
|
-
await
|
|
22605
|
+
await writeFile27(this.taskPath(id), this.serializeTask(next), "utf-8");
|
|
22097
22606
|
return next;
|
|
22098
22607
|
}
|
|
22099
22608
|
async transitionTask(id, nextStatus, now = /* @__PURE__ */ new Date()) {
|
|
@@ -22133,7 +22642,7 @@ ${project.description}
|
|
|
22133
22642
|
createdAt: timestamp,
|
|
22134
22643
|
updatedAt: timestamp
|
|
22135
22644
|
};
|
|
22136
|
-
await
|
|
22645
|
+
await writeFile27(this.projectPath(project.id), this.serializeProject(project), "utf-8");
|
|
22137
22646
|
return project;
|
|
22138
22647
|
}
|
|
22139
22648
|
async getProject(id) {
|
|
@@ -22150,7 +22659,7 @@ ${project.description}
|
|
|
22150
22659
|
const out = [];
|
|
22151
22660
|
for (const entry of entries) {
|
|
22152
22661
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
22153
|
-
const raw = await readFile24(
|
|
22662
|
+
const raw = await readFile24(path38.join(this.projectsDir, entry.name), "utf-8");
|
|
22154
22663
|
const project = this.parseProject(raw);
|
|
22155
22664
|
if (project) out.push(project);
|
|
22156
22665
|
}
|
|
@@ -22167,7 +22676,7 @@ ${project.description}
|
|
|
22167
22676
|
taskIds: patch.taskIds ? [...patch.taskIds].sort() : existing.taskIds,
|
|
22168
22677
|
updatedAt: now.toISOString()
|
|
22169
22678
|
};
|
|
22170
|
-
await
|
|
22679
|
+
await writeFile27(this.projectPath(id), this.serializeProject(next), "utf-8");
|
|
22171
22680
|
return next;
|
|
22172
22681
|
}
|
|
22173
22682
|
async deleteProject(id) {
|
|
@@ -23706,7 +24215,7 @@ Best for:
|
|
|
23706
24215
|
- Reviewing identity development over time`,
|
|
23707
24216
|
parameters: Type.Object({}),
|
|
23708
24217
|
async execute() {
|
|
23709
|
-
const workspaceDir =
|
|
24218
|
+
const workspaceDir = path39.join(process.env.HOME ?? "~", ".openclaw", "workspace");
|
|
23710
24219
|
const identity = await orchestrator.storage.readIdentity(workspaceDir);
|
|
23711
24220
|
if (!identity) {
|
|
23712
24221
|
return toolResult("No identity file found. Identity reflections build automatically through conversations when identityEnabled is true.");
|
|
@@ -24223,13 +24732,13 @@ promotionCandidates: ${res.promotionCandidateCount}`
|
|
|
24223
24732
|
}
|
|
24224
24733
|
|
|
24225
24734
|
// src/cli.ts
|
|
24226
|
-
import
|
|
24735
|
+
import path57 from "path";
|
|
24227
24736
|
import { access as access3, readFile as readFile37, readdir as readdir24, unlink as unlink7 } from "fs/promises";
|
|
24228
24737
|
import { createHash as createHash10 } from "crypto";
|
|
24229
24738
|
|
|
24230
24739
|
// src/transfer/export-json.ts
|
|
24231
|
-
import
|
|
24232
|
-
import { mkdir as
|
|
24740
|
+
import path41 from "path";
|
|
24741
|
+
import { mkdir as mkdir29, readFile as readFile26 } from "fs/promises";
|
|
24233
24742
|
|
|
24234
24743
|
// src/transfer/constants.ts
|
|
24235
24744
|
var EXPORT_FORMAT = "openclaw-engram-export";
|
|
@@ -24237,8 +24746,8 @@ var EXPORT_SCHEMA_VERSION = 1;
|
|
|
24237
24746
|
|
|
24238
24747
|
// src/transfer/fs-utils.ts
|
|
24239
24748
|
import { createHash as createHash8 } from "crypto";
|
|
24240
|
-
import { mkdir as
|
|
24241
|
-
import
|
|
24749
|
+
import { mkdir as mkdir28, readdir as readdir18, readFile as readFile25, stat as stat8, writeFile as writeFile28 } from "fs/promises";
|
|
24750
|
+
import path40 from "path";
|
|
24242
24751
|
async function sha256File(filePath) {
|
|
24243
24752
|
const buf = await readFile25(filePath);
|
|
24244
24753
|
const sha256 = createHash8("sha256").update(buf).digest("hex");
|
|
@@ -24250,8 +24759,8 @@ function sha256String(content) {
|
|
|
24250
24759
|
return { sha256, bytes: buf.byteLength };
|
|
24251
24760
|
}
|
|
24252
24761
|
async function writeJsonFile(filePath, value) {
|
|
24253
|
-
await
|
|
24254
|
-
await
|
|
24762
|
+
await mkdir28(path40.dirname(filePath), { recursive: true });
|
|
24763
|
+
await writeFile28(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
24255
24764
|
}
|
|
24256
24765
|
async function readJsonFile2(filePath) {
|
|
24257
24766
|
const raw = await readFile25(filePath, "utf-8");
|
|
@@ -24262,7 +24771,7 @@ async function listFilesRecursive(rootDir) {
|
|
|
24262
24771
|
async function walk(dir) {
|
|
24263
24772
|
const entries = await readdir18(dir, { withFileTypes: true });
|
|
24264
24773
|
for (const ent of entries) {
|
|
24265
|
-
const fp =
|
|
24774
|
+
const fp = path40.join(dir, ent.name);
|
|
24266
24775
|
if (ent.isDirectory()) {
|
|
24267
24776
|
await walk(fp);
|
|
24268
24777
|
} else if (ent.isFile()) {
|
|
@@ -24282,11 +24791,11 @@ async function fileExists(filePath) {
|
|
|
24282
24791
|
}
|
|
24283
24792
|
}
|
|
24284
24793
|
function toPosixRelPath(absPath, rootDir) {
|
|
24285
|
-
const rel =
|
|
24286
|
-
return rel.split(
|
|
24794
|
+
const rel = path40.relative(rootDir, absPath);
|
|
24795
|
+
return rel.split(path40.sep).join("/");
|
|
24287
24796
|
}
|
|
24288
24797
|
function fromPosixRelPath(relPath) {
|
|
24289
|
-
return relPath.split("/").join(
|
|
24798
|
+
return relPath.split("/").join(path40.sep);
|
|
24290
24799
|
}
|
|
24291
24800
|
|
|
24292
24801
|
// src/transfer/export-json.ts
|
|
@@ -24302,9 +24811,9 @@ function shouldExclude(relPosix, includeTranscripts) {
|
|
|
24302
24811
|
}
|
|
24303
24812
|
async function exportJsonBundle(opts) {
|
|
24304
24813
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
24305
|
-
const outDirAbs =
|
|
24306
|
-
await
|
|
24307
|
-
const memoryDirAbs =
|
|
24814
|
+
const outDirAbs = path41.resolve(opts.outDir);
|
|
24815
|
+
await mkdir29(outDirAbs, { recursive: true });
|
|
24816
|
+
const memoryDirAbs = path41.resolve(opts.memoryDir);
|
|
24308
24817
|
const filesAbs = await listFilesRecursive(memoryDirAbs);
|
|
24309
24818
|
const records = [];
|
|
24310
24819
|
const manifestFiles = [];
|
|
@@ -24317,7 +24826,7 @@ async function exportJsonBundle(opts) {
|
|
|
24317
24826
|
manifestFiles.push({ path: relPosix, sha256, bytes });
|
|
24318
24827
|
}
|
|
24319
24828
|
if (opts.includeWorkspaceIdentity !== false && opts.workspaceDir) {
|
|
24320
|
-
const identityPath =
|
|
24829
|
+
const identityPath = path41.join(opts.workspaceDir, "IDENTITY.md");
|
|
24321
24830
|
try {
|
|
24322
24831
|
const content = await readFile26(identityPath, "utf-8");
|
|
24323
24832
|
const relPath = "workspace/IDENTITY.md";
|
|
@@ -24336,13 +24845,13 @@ async function exportJsonBundle(opts) {
|
|
|
24336
24845
|
files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
|
|
24337
24846
|
};
|
|
24338
24847
|
const bundle = { manifest, records };
|
|
24339
|
-
await writeJsonFile(
|
|
24340
|
-
await writeJsonFile(
|
|
24848
|
+
await writeJsonFile(path41.join(outDirAbs, "manifest.json"), manifest);
|
|
24849
|
+
await writeJsonFile(path41.join(outDirAbs, "bundle.json"), bundle);
|
|
24341
24850
|
}
|
|
24342
24851
|
|
|
24343
24852
|
// src/transfer/export-md.ts
|
|
24344
|
-
import
|
|
24345
|
-
import { mkdir as
|
|
24853
|
+
import path42 from "path";
|
|
24854
|
+
import { mkdir as mkdir30, readFile as readFile27, writeFile as writeFile29 } from "fs/promises";
|
|
24346
24855
|
function shouldExclude2(relPosix, includeTranscripts) {
|
|
24347
24856
|
const parts = relPosix.split("/");
|
|
24348
24857
|
if (!includeTranscripts && parts[0] === "transcripts") return true;
|
|
@@ -24350,18 +24859,18 @@ function shouldExclude2(relPosix, includeTranscripts) {
|
|
|
24350
24859
|
}
|
|
24351
24860
|
async function exportMarkdownBundle(opts) {
|
|
24352
24861
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
24353
|
-
const outDirAbs =
|
|
24354
|
-
await
|
|
24355
|
-
const memDirAbs =
|
|
24862
|
+
const outDirAbs = path42.resolve(opts.outDir);
|
|
24863
|
+
await mkdir30(outDirAbs, { recursive: true });
|
|
24864
|
+
const memDirAbs = path42.resolve(opts.memoryDir);
|
|
24356
24865
|
const filesAbs = await listFilesRecursive(memDirAbs);
|
|
24357
24866
|
const manifestFiles = [];
|
|
24358
24867
|
for (const abs of filesAbs) {
|
|
24359
24868
|
const relPosix = toPosixRelPath(abs, memDirAbs);
|
|
24360
24869
|
if (shouldExclude2(relPosix, includeTranscripts)) continue;
|
|
24361
|
-
const dstAbs =
|
|
24362
|
-
await
|
|
24870
|
+
const dstAbs = path42.join(outDirAbs, ...relPosix.split("/"));
|
|
24871
|
+
await mkdir30(path42.dirname(dstAbs), { recursive: true });
|
|
24363
24872
|
const content = await readFile27(abs);
|
|
24364
|
-
await
|
|
24873
|
+
await writeFile29(dstAbs, content);
|
|
24365
24874
|
const { sha256, bytes } = await sha256File(abs);
|
|
24366
24875
|
manifestFiles.push({ path: relPosix, sha256, bytes });
|
|
24367
24876
|
}
|
|
@@ -24373,12 +24882,12 @@ async function exportMarkdownBundle(opts) {
|
|
|
24373
24882
|
includesTranscripts: includeTranscripts,
|
|
24374
24883
|
files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
|
|
24375
24884
|
};
|
|
24376
|
-
await writeJsonFile(
|
|
24885
|
+
await writeJsonFile(path42.join(outDirAbs, "manifest.json"), manifest);
|
|
24377
24886
|
}
|
|
24378
24887
|
async function looksLikeEngramMdExport(fromDir) {
|
|
24379
|
-
const dirAbs =
|
|
24888
|
+
const dirAbs = path42.resolve(fromDir);
|
|
24380
24889
|
try {
|
|
24381
|
-
const raw = await readFile27(
|
|
24890
|
+
const raw = await readFile27(path42.join(dirAbs, "manifest.json"), "utf-8");
|
|
24382
24891
|
const parsed = JSON.parse(raw);
|
|
24383
24892
|
return parsed.format === EXPORT_FORMAT && parsed.schemaVersion === EXPORT_SCHEMA_VERSION;
|
|
24384
24893
|
} catch {
|
|
@@ -24387,16 +24896,16 @@ async function looksLikeEngramMdExport(fromDir) {
|
|
|
24387
24896
|
}
|
|
24388
24897
|
|
|
24389
24898
|
// src/transfer/backup.ts
|
|
24390
|
-
import
|
|
24391
|
-
import { mkdir as
|
|
24899
|
+
import path43 from "path";
|
|
24900
|
+
import { mkdir as mkdir31, readdir as readdir19, rm as rm5 } from "fs/promises";
|
|
24392
24901
|
function timestampDirName(now) {
|
|
24393
24902
|
return now.toISOString().replace(/[:.]/g, "-");
|
|
24394
24903
|
}
|
|
24395
24904
|
async function backupMemoryDir(opts) {
|
|
24396
|
-
const outDirAbs =
|
|
24397
|
-
await
|
|
24905
|
+
const outDirAbs = path43.resolve(opts.outDir);
|
|
24906
|
+
await mkdir31(outDirAbs, { recursive: true });
|
|
24398
24907
|
const ts = timestampDirName(/* @__PURE__ */ new Date());
|
|
24399
|
-
const backupDir =
|
|
24908
|
+
const backupDir = path43.join(outDirAbs, ts);
|
|
24400
24909
|
await exportMarkdownBundle({
|
|
24401
24910
|
memoryDir: opts.memoryDir,
|
|
24402
24911
|
outDir: backupDir,
|
|
@@ -24421,13 +24930,13 @@ async function enforceRetention(outDirAbs, retentionDays) {
|
|
|
24421
24930
|
const tsMs = iso ? Date.parse(iso) : NaN;
|
|
24422
24931
|
if (!Number.isFinite(tsMs)) continue;
|
|
24423
24932
|
if (tsMs < cutoffMs) {
|
|
24424
|
-
await rm5(
|
|
24933
|
+
await rm5(path43.join(outDirAbs, name), { recursive: true, force: true });
|
|
24425
24934
|
}
|
|
24426
24935
|
}
|
|
24427
24936
|
}
|
|
24428
24937
|
|
|
24429
24938
|
// src/transfer/export-sqlite.ts
|
|
24430
|
-
import
|
|
24939
|
+
import path44 from "path";
|
|
24431
24940
|
import Database from "better-sqlite3";
|
|
24432
24941
|
import { readFile as readFile28 } from "fs/promises";
|
|
24433
24942
|
|
|
@@ -24455,8 +24964,8 @@ function shouldExclude3(relPosix, includeTranscripts) {
|
|
|
24455
24964
|
}
|
|
24456
24965
|
async function exportSqlite(opts) {
|
|
24457
24966
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
24458
|
-
const memDirAbs =
|
|
24459
|
-
const outAbs =
|
|
24967
|
+
const memDirAbs = path44.resolve(opts.memoryDir);
|
|
24968
|
+
const outAbs = path44.resolve(opts.outFile);
|
|
24460
24969
|
const filesAbs = await listFilesRecursive(memDirAbs);
|
|
24461
24970
|
const db = new Database(outAbs);
|
|
24462
24971
|
try {
|
|
@@ -24488,8 +24997,8 @@ async function exportSqlite(opts) {
|
|
|
24488
24997
|
}
|
|
24489
24998
|
|
|
24490
24999
|
// src/transfer/import-json.ts
|
|
24491
|
-
import
|
|
24492
|
-
import { mkdir as
|
|
25000
|
+
import path45 from "path";
|
|
25001
|
+
import { mkdir as mkdir32, writeFile as writeFile30 } from "fs/promises";
|
|
24493
25002
|
|
|
24494
25003
|
// src/transfer/types.ts
|
|
24495
25004
|
import { z as z4 } from "zod";
|
|
@@ -24522,21 +25031,21 @@ function normalizeForDedupe(s) {
|
|
|
24522
25031
|
}
|
|
24523
25032
|
async function importJsonBundle(opts) {
|
|
24524
25033
|
const conflict = opts.conflict ?? "skip";
|
|
24525
|
-
const fromDirAbs =
|
|
24526
|
-
const bundlePath =
|
|
25034
|
+
const fromDirAbs = path45.resolve(opts.fromDir);
|
|
25035
|
+
const bundlePath = path45.join(fromDirAbs, "bundle.json");
|
|
24527
25036
|
const bundle = ExportBundleV1Schema.parse(await readJsonFile2(bundlePath));
|
|
24528
|
-
const memDirAbs =
|
|
25037
|
+
const memDirAbs = path45.resolve(opts.targetMemoryDir);
|
|
24529
25038
|
const written = [];
|
|
24530
25039
|
let skipped = 0;
|
|
24531
25040
|
for (const rec of bundle.records) {
|
|
24532
25041
|
const isWorkspace = rec.path.startsWith("workspace/");
|
|
24533
|
-
const targetBase = isWorkspace ? opts.workspaceDir ?
|
|
25042
|
+
const targetBase = isWorkspace ? opts.workspaceDir ? path45.resolve(opts.workspaceDir) : null : memDirAbs;
|
|
24534
25043
|
if (isWorkspace && !targetBase) {
|
|
24535
25044
|
skipped += 1;
|
|
24536
25045
|
continue;
|
|
24537
25046
|
}
|
|
24538
25047
|
const relFs = fromPosixRelPath(isWorkspace ? rec.path.replace(/^workspace\//, "") : rec.path);
|
|
24539
|
-
const absTarget =
|
|
25048
|
+
const absTarget = path45.join(targetBase, relFs);
|
|
24540
25049
|
const exists3 = await fileExists(absTarget);
|
|
24541
25050
|
if (exists3) {
|
|
24542
25051
|
if (conflict === "skip") {
|
|
@@ -24560,30 +25069,30 @@ async function importJsonBundle(opts) {
|
|
|
24560
25069
|
return { written: 0, skipped };
|
|
24561
25070
|
}
|
|
24562
25071
|
for (const w of written) {
|
|
24563
|
-
await
|
|
24564
|
-
await
|
|
25072
|
+
await mkdir32(path45.dirname(w.abs), { recursive: true });
|
|
25073
|
+
await writeFile30(w.abs, w.content, "utf-8");
|
|
24565
25074
|
}
|
|
24566
25075
|
return { written: written.length, skipped };
|
|
24567
25076
|
}
|
|
24568
25077
|
function looksLikeEngramJsonExport(fromDir) {
|
|
24569
|
-
const dir =
|
|
25078
|
+
const dir = path45.resolve(fromDir);
|
|
24570
25079
|
return Promise.all([
|
|
24571
|
-
fileExists(
|
|
24572
|
-
fileExists(
|
|
25080
|
+
fileExists(path45.join(dir, "manifest.json")),
|
|
25081
|
+
fileExists(path45.join(dir, "bundle.json"))
|
|
24573
25082
|
]).then(([m, b]) => m && b);
|
|
24574
25083
|
}
|
|
24575
25084
|
|
|
24576
25085
|
// src/transfer/import-sqlite.ts
|
|
24577
|
-
import
|
|
25086
|
+
import path46 from "path";
|
|
24578
25087
|
import Database2 from "better-sqlite3";
|
|
24579
|
-
import { mkdir as
|
|
25088
|
+
import { mkdir as mkdir33, writeFile as writeFile31 } from "fs/promises";
|
|
24580
25089
|
function normalizeForDedupe2(s) {
|
|
24581
25090
|
return s.replace(/\s+/g, " ").trim();
|
|
24582
25091
|
}
|
|
24583
25092
|
async function importSqlite(opts) {
|
|
24584
25093
|
const conflict = opts.conflict ?? "skip";
|
|
24585
|
-
const memDirAbs =
|
|
24586
|
-
const fromAbs =
|
|
25094
|
+
const memDirAbs = path46.resolve(opts.targetMemoryDir);
|
|
25095
|
+
const fromAbs = path46.resolve(opts.fromFile);
|
|
24587
25096
|
const db = new Database2(fromAbs, { readonly: true });
|
|
24588
25097
|
const written = [];
|
|
24589
25098
|
let skipped = 0;
|
|
@@ -24596,7 +25105,7 @@ async function importSqlite(opts) {
|
|
|
24596
25105
|
const rows = db.prepare("SELECT path_rel, content FROM files").all();
|
|
24597
25106
|
for (const r of rows) {
|
|
24598
25107
|
const relFs = fromPosixRelPath(r.path_rel);
|
|
24599
|
-
const absTarget =
|
|
25108
|
+
const absTarget = path46.join(memDirAbs, relFs);
|
|
24600
25109
|
const exists3 = await fileExists(absTarget);
|
|
24601
25110
|
if (exists3) {
|
|
24602
25111
|
if (conflict === "skip") {
|
|
@@ -24621,29 +25130,29 @@ async function importSqlite(opts) {
|
|
|
24621
25130
|
}
|
|
24622
25131
|
if (opts.dryRun) return { written: 0, skipped };
|
|
24623
25132
|
for (const w of written) {
|
|
24624
|
-
await
|
|
24625
|
-
await
|
|
25133
|
+
await mkdir33(path46.dirname(w.abs), { recursive: true });
|
|
25134
|
+
await writeFile31(w.abs, w.content, "utf-8");
|
|
24626
25135
|
}
|
|
24627
25136
|
return { written: written.length, skipped };
|
|
24628
25137
|
}
|
|
24629
25138
|
|
|
24630
25139
|
// src/transfer/import-md.ts
|
|
24631
|
-
import
|
|
24632
|
-
import { mkdir as
|
|
25140
|
+
import path47 from "path";
|
|
25141
|
+
import { mkdir as mkdir34, readFile as readFile29, writeFile as writeFile32 } from "fs/promises";
|
|
24633
25142
|
function normalizeForDedupe3(s) {
|
|
24634
25143
|
return s.replace(/\s+/g, " ").trim();
|
|
24635
25144
|
}
|
|
24636
25145
|
async function importMarkdownBundle(opts) {
|
|
24637
25146
|
const conflict = opts.conflict ?? "skip";
|
|
24638
|
-
const fromAbs =
|
|
24639
|
-
const targetAbs =
|
|
25147
|
+
const fromAbs = path47.resolve(opts.fromDir);
|
|
25148
|
+
const targetAbs = path47.resolve(opts.targetMemoryDir);
|
|
24640
25149
|
const filesAbs = await listFilesRecursive(fromAbs);
|
|
24641
25150
|
const writes = [];
|
|
24642
25151
|
let skipped = 0;
|
|
24643
25152
|
for (const abs of filesAbs) {
|
|
24644
25153
|
const relPosix = toPosixRelPath(abs, fromAbs);
|
|
24645
25154
|
if (relPosix === "manifest.json") continue;
|
|
24646
|
-
const dstAbs =
|
|
25155
|
+
const dstAbs = path47.join(targetAbs, fromPosixRelPath(relPosix));
|
|
24647
25156
|
const content = await readFile29(abs, "utf-8");
|
|
24648
25157
|
const exists3 = await fileExists(dstAbs);
|
|
24649
25158
|
if (exists3) {
|
|
@@ -24666,17 +25175,17 @@ async function importMarkdownBundle(opts) {
|
|
|
24666
25175
|
}
|
|
24667
25176
|
if (opts.dryRun) return { written: 0, skipped };
|
|
24668
25177
|
for (const w of writes) {
|
|
24669
|
-
await
|
|
24670
|
-
await
|
|
25178
|
+
await mkdir34(path47.dirname(w.abs), { recursive: true });
|
|
25179
|
+
await writeFile32(w.abs, w.content, "utf-8");
|
|
24671
25180
|
}
|
|
24672
25181
|
return { written: writes.length, skipped };
|
|
24673
25182
|
}
|
|
24674
25183
|
|
|
24675
25184
|
// src/transfer/autodetect.ts
|
|
24676
|
-
import
|
|
25185
|
+
import path48 from "path";
|
|
24677
25186
|
import { stat as stat9 } from "fs/promises";
|
|
24678
25187
|
async function detectImportFormat(fromPath) {
|
|
24679
|
-
const abs =
|
|
25188
|
+
const abs = path48.resolve(fromPath);
|
|
24680
25189
|
let st;
|
|
24681
25190
|
try {
|
|
24682
25191
|
st = await stat9(abs);
|
|
@@ -25104,8 +25613,8 @@ function gatherCandidates(input, warnings) {
|
|
|
25104
25613
|
const record = rec;
|
|
25105
25614
|
const content = typeof record.content === "string" ? record.content : null;
|
|
25106
25615
|
if (!content) continue;
|
|
25107
|
-
const
|
|
25108
|
-
if (!
|
|
25616
|
+
const path59 = typeof record.path === "string" ? record.path : "";
|
|
25617
|
+
if (!path59.startsWith("transcripts/") && !path59.includes("/transcripts/")) continue;
|
|
25109
25618
|
rows.push(...parseJsonl(content, warnings));
|
|
25110
25619
|
}
|
|
25111
25620
|
return rows;
|
|
@@ -25161,8 +25670,8 @@ var openclawReplayNormalizer = {
|
|
|
25161
25670
|
};
|
|
25162
25671
|
|
|
25163
25672
|
// src/maintenance/archive-observations.ts
|
|
25164
|
-
import
|
|
25165
|
-
import { mkdir as
|
|
25673
|
+
import path49 from "path";
|
|
25674
|
+
import { mkdir as mkdir35, readdir as readdir20, readFile as readFile30, unlink as unlink6, writeFile as writeFile33 } from "fs/promises";
|
|
25166
25675
|
var DATE_FILE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})\.(jsonl|md)$/;
|
|
25167
25676
|
function normalizeRetentionDays(value) {
|
|
25168
25677
|
if (!Number.isFinite(value)) return 30;
|
|
@@ -25188,8 +25697,8 @@ async function listFilesRecursive2(root, relPrefix = "") {
|
|
|
25188
25697
|
return out;
|
|
25189
25698
|
}
|
|
25190
25699
|
for (const entry of entries) {
|
|
25191
|
-
const rel = relPrefix ?
|
|
25192
|
-
const full =
|
|
25700
|
+
const rel = relPrefix ? path49.join(relPrefix, entry.name) : entry.name;
|
|
25701
|
+
const full = path49.join(root, entry.name);
|
|
25193
25702
|
if (entry.isDirectory()) {
|
|
25194
25703
|
out.push(...await listFilesRecursive2(full, rel));
|
|
25195
25704
|
continue;
|
|
@@ -25199,19 +25708,19 @@ async function listFilesRecursive2(root, relPrefix = "") {
|
|
|
25199
25708
|
return out;
|
|
25200
25709
|
}
|
|
25201
25710
|
async function collectArchiveCandidates(memoryDir, cutoffTimeMs) {
|
|
25202
|
-
const roots = ["transcripts",
|
|
25711
|
+
const roots = ["transcripts", path49.join("state", "tool-usage"), path49.join("summaries", "hourly")];
|
|
25203
25712
|
const out = [];
|
|
25204
25713
|
for (const relRoot of roots) {
|
|
25205
|
-
const absRoot =
|
|
25714
|
+
const absRoot = path49.join(memoryDir, relRoot);
|
|
25206
25715
|
const files = await listFilesRecursive2(absRoot);
|
|
25207
25716
|
for (const fileRel of files) {
|
|
25208
|
-
const filename =
|
|
25717
|
+
const filename = path49.basename(fileRel);
|
|
25209
25718
|
const parsedDate = extractDateFromFilename(filename);
|
|
25210
25719
|
if (!parsedDate) continue;
|
|
25211
25720
|
if (parsedDate.getTime() >= cutoffTimeMs) continue;
|
|
25212
25721
|
out.push({
|
|
25213
|
-
absolutePath:
|
|
25214
|
-
relativePath:
|
|
25722
|
+
absolutePath: path49.join(absRoot, fileRel),
|
|
25723
|
+
relativePath: path49.join(relRoot, fileRel)
|
|
25215
25724
|
});
|
|
25216
25725
|
}
|
|
25217
25726
|
}
|
|
@@ -25226,7 +25735,7 @@ async function archiveObservations(options) {
|
|
|
25226
25735
|
new Date(now.getTime() - retentionDays * 24 * 60 * 60 * 1e3)
|
|
25227
25736
|
);
|
|
25228
25737
|
const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
25229
|
-
const archiveRoot =
|
|
25738
|
+
const archiveRoot = path49.join(options.memoryDir, "archive", "observations", stamp);
|
|
25230
25739
|
const candidates = retentionDays === 0 ? [] : await collectArchiveCandidates(
|
|
25231
25740
|
options.memoryDir,
|
|
25232
25741
|
cutoffDayStartUtc
|
|
@@ -25235,13 +25744,13 @@ async function archiveObservations(options) {
|
|
|
25235
25744
|
let archivedBytes = 0;
|
|
25236
25745
|
const archivedRelativePaths = [];
|
|
25237
25746
|
if (!dryRun && candidates.length > 0) {
|
|
25238
|
-
await
|
|
25747
|
+
await mkdir35(archiveRoot, { recursive: true });
|
|
25239
25748
|
for (const candidate of candidates) {
|
|
25240
|
-
const archivePath =
|
|
25241
|
-
const archiveDir =
|
|
25242
|
-
await
|
|
25749
|
+
const archivePath = path49.join(archiveRoot, candidate.relativePath);
|
|
25750
|
+
const archiveDir = path49.dirname(archivePath);
|
|
25751
|
+
await mkdir35(archiveDir, { recursive: true });
|
|
25243
25752
|
const raw = await readFile30(candidate.absolutePath);
|
|
25244
|
-
await
|
|
25753
|
+
await writeFile33(archivePath, raw);
|
|
25245
25754
|
await unlink6(candidate.absolutePath);
|
|
25246
25755
|
archivedFiles += 1;
|
|
25247
25756
|
archivedBytes += raw.byteLength;
|
|
@@ -25262,12 +25771,12 @@ async function archiveObservations(options) {
|
|
|
25262
25771
|
}
|
|
25263
25772
|
|
|
25264
25773
|
// src/maintenance/rebuild-observations.ts
|
|
25265
|
-
import
|
|
25774
|
+
import path51 from "path";
|
|
25266
25775
|
import { readdir as readdir21, readFile as readFile32 } from "fs/promises";
|
|
25267
25776
|
|
|
25268
25777
|
// src/maintenance/observation-ledger-utils.ts
|
|
25269
|
-
import
|
|
25270
|
-
import { mkdir as
|
|
25778
|
+
import path50 from "path";
|
|
25779
|
+
import { mkdir as mkdir36, readFile as readFile31, writeFile as writeFile34 } from "fs/promises";
|
|
25271
25780
|
function toHourBucketIso(timestamp) {
|
|
25272
25781
|
const normalized = /(?:Z|[+-]\d{2}:\d{2})$/u.test(timestamp) ? timestamp : `${timestamp}Z`;
|
|
25273
25782
|
const ms = Date.parse(normalized);
|
|
@@ -25278,8 +25787,8 @@ function toHourBucketIso(timestamp) {
|
|
|
25278
25787
|
}
|
|
25279
25788
|
async function backupAndWriteRebuiltObservations(options) {
|
|
25280
25789
|
const stamp = options.now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
25281
|
-
const archiveRoot =
|
|
25282
|
-
let backupPath =
|
|
25790
|
+
const archiveRoot = path50.join(options.memoryDir, "archive", "observations", stamp);
|
|
25791
|
+
let backupPath = path50.join(
|
|
25283
25792
|
archiveRoot,
|
|
25284
25793
|
"state",
|
|
25285
25794
|
"observation-ledger",
|
|
@@ -25287,8 +25796,8 @@ async function backupAndWriteRebuiltObservations(options) {
|
|
|
25287
25796
|
);
|
|
25288
25797
|
try {
|
|
25289
25798
|
const existing = await readFile31(options.outputPath, "utf-8");
|
|
25290
|
-
await
|
|
25291
|
-
await
|
|
25799
|
+
await mkdir36(path50.dirname(backupPath), { recursive: true });
|
|
25800
|
+
await writeFile34(backupPath, existing, "utf-8");
|
|
25292
25801
|
} catch (err) {
|
|
25293
25802
|
const code = err.code;
|
|
25294
25803
|
if (code && code === "ENOENT") {
|
|
@@ -25304,8 +25813,8 @@ async function backupAndWriteRebuiltObservations(options) {
|
|
|
25304
25813
|
rebuiltAt
|
|
25305
25814
|
})
|
|
25306
25815
|
);
|
|
25307
|
-
await
|
|
25308
|
-
await
|
|
25816
|
+
await mkdir36(path50.dirname(options.outputPath), { recursive: true });
|
|
25817
|
+
await writeFile34(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
|
|
25309
25818
|
` : "", "utf-8");
|
|
25310
25819
|
return backupPath;
|
|
25311
25820
|
}
|
|
@@ -25327,7 +25836,7 @@ async function listTranscriptFiles2(root) {
|
|
|
25327
25836
|
for (const entry of entries) {
|
|
25328
25837
|
if (entry.name === "." || entry.name === "..") continue;
|
|
25329
25838
|
if (entry.isSymbolicLink()) continue;
|
|
25330
|
-
const full =
|
|
25839
|
+
const full = path51.join(root, entry.name);
|
|
25331
25840
|
if (entry.isDirectory()) {
|
|
25332
25841
|
out.push(...await listTranscriptFiles2(full));
|
|
25333
25842
|
continue;
|
|
@@ -25387,8 +25896,8 @@ function buildLedgerRows(linesByFile) {
|
|
|
25387
25896
|
async function rebuildObservations(options) {
|
|
25388
25897
|
const dryRun = options.dryRun !== false;
|
|
25389
25898
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
25390
|
-
const transcriptsRoot =
|
|
25391
|
-
const outputPath =
|
|
25899
|
+
const transcriptsRoot = path51.join(options.memoryDir, "transcripts");
|
|
25900
|
+
const outputPath = path51.join(
|
|
25392
25901
|
options.memoryDir,
|
|
25393
25902
|
"state",
|
|
25394
25903
|
"observation-ledger",
|
|
@@ -25424,7 +25933,7 @@ async function rebuildObservations(options) {
|
|
|
25424
25933
|
}
|
|
25425
25934
|
|
|
25426
25935
|
// src/maintenance/migrate-observations.ts
|
|
25427
|
-
import
|
|
25936
|
+
import path52 from "path";
|
|
25428
25937
|
import { readdir as readdir22, readFile as readFile33 } from "fs/promises";
|
|
25429
25938
|
function toNonNegativeInt(value) {
|
|
25430
25939
|
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
@@ -25488,15 +25997,15 @@ async function listLegacyObservationFiles(root) {
|
|
|
25488
25997
|
async function migrateObservations(options) {
|
|
25489
25998
|
const dryRun = options.dryRun !== false;
|
|
25490
25999
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
25491
|
-
const ledgerRoot =
|
|
25492
|
-
const outputPath =
|
|
26000
|
+
const ledgerRoot = path52.join(options.memoryDir, "state", "observation-ledger");
|
|
26001
|
+
const outputPath = path52.join(ledgerRoot, "rebuilt-observations.jsonl");
|
|
25493
26002
|
const legacyFiles = await listLegacyObservationFiles(ledgerRoot);
|
|
25494
|
-
const sourceRelativePaths = legacyFiles.map((name) =>
|
|
26003
|
+
const sourceRelativePaths = legacyFiles.map((name) => path52.join("state", "observation-ledger", name));
|
|
25495
26004
|
const byKey = /* @__PURE__ */ new Map();
|
|
25496
26005
|
let parsedRows = 0;
|
|
25497
26006
|
let malformedLines = 0;
|
|
25498
26007
|
for (const file of legacyFiles) {
|
|
25499
|
-
const full =
|
|
26008
|
+
const full = path52.join(ledgerRoot, file);
|
|
25500
26009
|
const raw = await readFile33(full, "utf-8");
|
|
25501
26010
|
for (const line of raw.split("\n")) {
|
|
25502
26011
|
if (!line.trim()) continue;
|
|
@@ -25686,10 +26195,10 @@ var defaultCommandRunner = (command, args, options) => {
|
|
|
25686
26195
|
|
|
25687
26196
|
// src/network/webdav.ts
|
|
25688
26197
|
import { createReadStream } from "fs";
|
|
25689
|
-
import { mkdir as
|
|
26198
|
+
import { mkdir as mkdir37, readdir as readdir23, realpath as realpath2, stat as stat11 } from "fs/promises";
|
|
25690
26199
|
import { createServer } from "http";
|
|
25691
26200
|
import { timingSafeEqual } from "crypto";
|
|
25692
|
-
import
|
|
26201
|
+
import path53 from "path";
|
|
25693
26202
|
import { pipeline } from "stream/promises";
|
|
25694
26203
|
import { URL as URL2 } from "url";
|
|
25695
26204
|
function hostToUrlAuthority(host) {
|
|
@@ -25725,10 +26234,10 @@ var WebDavServer = class _WebDavServer {
|
|
|
25725
26234
|
const allowedRoots = [];
|
|
25726
26235
|
const aliasSet = /* @__PURE__ */ new Set();
|
|
25727
26236
|
for (const dir of options.allowlistDirs) {
|
|
25728
|
-
const resolved =
|
|
25729
|
-
await
|
|
26237
|
+
const resolved = path53.resolve(dir);
|
|
26238
|
+
await mkdir37(resolved, { recursive: true });
|
|
25730
26239
|
const canonical = await realpath2(resolved);
|
|
25731
|
-
const alias =
|
|
26240
|
+
const alias = path53.basename(canonical) || "root";
|
|
25732
26241
|
if (aliasSet.has(alias)) {
|
|
25733
26242
|
throw new Error(`duplicate webdav allowlist alias: ${alias}`);
|
|
25734
26243
|
}
|
|
@@ -25884,7 +26393,7 @@ var WebDavServer = class _WebDavServer {
|
|
|
25884
26393
|
if (decodedPath.includes("\0")) {
|
|
25885
26394
|
return { ok: false, code: 400, message: "invalid path" };
|
|
25886
26395
|
}
|
|
25887
|
-
const normalized =
|
|
26396
|
+
const normalized = path53.posix.normalize(decodedPath);
|
|
25888
26397
|
const segments = normalized.split("/").filter((segment) => segment.length > 0);
|
|
25889
26398
|
if (segments.length === 0) {
|
|
25890
26399
|
return { ok: false, code: 403, message: "root listing is not allowed" };
|
|
@@ -25898,7 +26407,7 @@ var WebDavServer = class _WebDavServer {
|
|
|
25898
26407
|
if (relative.some((segment) => segment === ".." || segment.includes("\\"))) {
|
|
25899
26408
|
return { ok: false, code: 403, message: "path traversal is not allowed" };
|
|
25900
26409
|
}
|
|
25901
|
-
const candidate =
|
|
26410
|
+
const candidate = path53.resolve(root.absolute, ...relative);
|
|
25902
26411
|
if (!this.isPathInside(root.absolute, candidate)) {
|
|
25903
26412
|
return { ok: false, code: 403, message: "path escaped allowlist" };
|
|
25904
26413
|
}
|
|
@@ -25976,10 +26485,10 @@ var WebDavServer = class _WebDavServer {
|
|
|
25976
26485
|
}
|
|
25977
26486
|
isPathInside(root, target) {
|
|
25978
26487
|
if (target === root) return true;
|
|
25979
|
-
if (root ===
|
|
26488
|
+
if (root === path53.parse(root).root) {
|
|
25980
26489
|
return target.startsWith(root);
|
|
25981
26490
|
}
|
|
25982
|
-
return target.startsWith(`${root}${
|
|
26491
|
+
return target.startsWith(`${root}${path53.sep}`);
|
|
25983
26492
|
}
|
|
25984
26493
|
};
|
|
25985
26494
|
function xmlEscape(value) {
|
|
@@ -25994,10 +26503,10 @@ import { createHash as createHash9 } from "crypto";
|
|
|
25994
26503
|
import { createServer as createServer2 } from "http";
|
|
25995
26504
|
import { watch } from "fs";
|
|
25996
26505
|
import { readFile as readFile35 } from "fs/promises";
|
|
25997
|
-
import
|
|
26506
|
+
import path55 from "path";
|
|
25998
26507
|
|
|
25999
26508
|
// src/graph-dashboard-parser.ts
|
|
26000
|
-
import
|
|
26509
|
+
import path54 from "path";
|
|
26001
26510
|
import { readFile as readFile34 } from "fs/promises";
|
|
26002
26511
|
|
|
26003
26512
|
// src/graph-dashboard-key.ts
|
|
@@ -26008,7 +26517,7 @@ function graphEdgeKey(edge) {
|
|
|
26008
26517
|
// src/graph-dashboard-parser.ts
|
|
26009
26518
|
var GRAPH_TYPES = ["entity", "time", "causal"];
|
|
26010
26519
|
function graphFile(memoryDir, type) {
|
|
26011
|
-
return
|
|
26520
|
+
return path54.join(memoryDir, "state", "graphs", `${type}.jsonl`);
|
|
26012
26521
|
}
|
|
26013
26522
|
function isGraphEdge(raw, expectedType) {
|
|
26014
26523
|
if (!raw || typeof raw !== "object") return false;
|
|
@@ -26135,7 +26644,7 @@ var GraphDashboardServer = class {
|
|
|
26135
26644
|
this.memoryDir = options.memoryDir;
|
|
26136
26645
|
this.host = options.host?.trim() || "127.0.0.1";
|
|
26137
26646
|
this.requestedPort = Number.isFinite(options.port) ? Math.max(0, Math.floor(options.port ?? 0)) : 0;
|
|
26138
|
-
this.publicDir = options.publicDir ??
|
|
26647
|
+
this.publicDir = options.publicDir ?? path55.join(process.cwd(), "dashboard", "public");
|
|
26139
26648
|
this.watchDebounceMs = Math.max(50, Math.floor(options.watchDebounceMs ?? 300));
|
|
26140
26649
|
}
|
|
26141
26650
|
async start() {
|
|
@@ -26234,11 +26743,11 @@ var GraphDashboardServer = class {
|
|
|
26234
26743
|
return;
|
|
26235
26744
|
}
|
|
26236
26745
|
if (req.method === "GET" && url === "/app.js") {
|
|
26237
|
-
await this.respondStatic(res,
|
|
26746
|
+
await this.respondStatic(res, path55.join(this.publicDir, "app.js"), "application/javascript; charset=utf-8");
|
|
26238
26747
|
return;
|
|
26239
26748
|
}
|
|
26240
26749
|
if (req.method === "GET" && (url === "/" || url === "/index.html")) {
|
|
26241
|
-
await this.respondStatic(res,
|
|
26750
|
+
await this.respondStatic(res, path55.join(this.publicDir, "index.html"), "text/html; charset=utf-8");
|
|
26242
26751
|
return;
|
|
26243
26752
|
}
|
|
26244
26753
|
this.respondJson(res, 404, { error: "Not found" });
|
|
@@ -26324,7 +26833,7 @@ var GraphDashboardServer = class {
|
|
|
26324
26833
|
}
|
|
26325
26834
|
}
|
|
26326
26835
|
startWatcher() {
|
|
26327
|
-
const graphDir =
|
|
26836
|
+
const graphDir = path55.join(this.memoryDir, "state", "graphs");
|
|
26328
26837
|
try {
|
|
26329
26838
|
this.watcher = watch(graphDir, { persistent: false }, () => {
|
|
26330
26839
|
this.scheduleRebuild();
|
|
@@ -26370,7 +26879,7 @@ var GraphDashboardServer = class {
|
|
|
26370
26879
|
|
|
26371
26880
|
// src/compat/checks.ts
|
|
26372
26881
|
import { access as access2, readFile as readFile36 } from "fs/promises";
|
|
26373
|
-
import
|
|
26882
|
+
import path56 from "path";
|
|
26374
26883
|
import { spawn as spawn4 } from "child_process";
|
|
26375
26884
|
var REQUIRED_HOOKS = ["before_agent_start", "agent_end"];
|
|
26376
26885
|
function isSafeCommandToken(command) {
|
|
@@ -26552,9 +27061,9 @@ function compareVersions(a, b) {
|
|
|
26552
27061
|
async function runCompatChecks(options) {
|
|
26553
27062
|
const checks = [];
|
|
26554
27063
|
const runner = options.runner ?? defaultRunner;
|
|
26555
|
-
const pluginJsonPath =
|
|
26556
|
-
const packageJsonPath =
|
|
26557
|
-
const indexPath =
|
|
27064
|
+
const pluginJsonPath = path56.join(options.repoRoot, "openclaw.plugin.json");
|
|
27065
|
+
const packageJsonPath = path56.join(options.repoRoot, "package.json");
|
|
27066
|
+
const indexPath = path56.join(options.repoRoot, "src", "index.ts");
|
|
26558
27067
|
let pluginRaw = "";
|
|
26559
27068
|
let pluginManifestPresent = false;
|
|
26560
27069
|
try {
|
|
@@ -26739,171 +27248,6 @@ async function runCompatChecks(options) {
|
|
|
26739
27248
|
};
|
|
26740
27249
|
}
|
|
26741
27250
|
|
|
26742
|
-
// src/abstraction-nodes.ts
|
|
26743
|
-
import path54 from "path";
|
|
26744
|
-
import { mkdir as mkdir36, writeFile as writeFile33 } from "fs/promises";
|
|
26745
|
-
function validateKind2(raw) {
|
|
26746
|
-
const value = assertString2(raw, "kind");
|
|
26747
|
-
if (!["episode", "topic", "project", "workflow", "constraint"].includes(value)) {
|
|
26748
|
-
throw new Error("kind must be one of episode|topic|project|workflow|constraint");
|
|
26749
|
-
}
|
|
26750
|
-
return value;
|
|
26751
|
-
}
|
|
26752
|
-
function validateLevel(raw) {
|
|
26753
|
-
const value = assertString2(raw, "abstractionLevel");
|
|
26754
|
-
if (!["micro", "meso", "macro"].includes(value)) {
|
|
26755
|
-
throw new Error("abstractionLevel must be one of micro|meso|macro");
|
|
26756
|
-
}
|
|
26757
|
-
return value;
|
|
26758
|
-
}
|
|
26759
|
-
function resolveAbstractionNodeStoreDir(memoryDir, overrideDir) {
|
|
26760
|
-
if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
|
|
26761
|
-
return overrideDir.trim();
|
|
26762
|
-
}
|
|
26763
|
-
return path54.join(memoryDir, "state", "abstraction-nodes");
|
|
26764
|
-
}
|
|
26765
|
-
function validateAbstractionNode(raw) {
|
|
26766
|
-
if (!isRecord2(raw)) throw new Error("abstraction node must be an object");
|
|
26767
|
-
if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
|
|
26768
|
-
return {
|
|
26769
|
-
schemaVersion: 1,
|
|
26770
|
-
nodeId: assertSafePathSegment(assertString2(raw.nodeId, "nodeId"), "nodeId"),
|
|
26771
|
-
recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
|
|
26772
|
-
sessionKey: assertString2(raw.sessionKey, "sessionKey"),
|
|
26773
|
-
kind: validateKind2(raw.kind),
|
|
26774
|
-
abstractionLevel: validateLevel(raw.abstractionLevel),
|
|
26775
|
-
title: assertString2(raw.title, "title"),
|
|
26776
|
-
summary: assertString2(raw.summary, "summary"),
|
|
26777
|
-
sourceMemoryIds: optionalStringArray2(raw.sourceMemoryIds, "sourceMemoryIds"),
|
|
26778
|
-
entityRefs: optionalStringArray2(raw.entityRefs, "entityRefs"),
|
|
26779
|
-
tags: optionalStringArray2(raw.tags, "tags"),
|
|
26780
|
-
metadata: validateStringRecord(raw.metadata, "metadata")
|
|
26781
|
-
};
|
|
26782
|
-
}
|
|
26783
|
-
async function getAbstractionNodeStoreStatus(options) {
|
|
26784
|
-
const rootDir = resolveAbstractionNodeStoreDir(options.memoryDir, options.abstractionNodeStoreDir);
|
|
26785
|
-
const nodesDir = path54.join(rootDir, "nodes");
|
|
26786
|
-
const files = await listJsonFiles(nodesDir);
|
|
26787
|
-
const nodes = [];
|
|
26788
|
-
const invalidNodes = [];
|
|
26789
|
-
for (const filePath of files) {
|
|
26790
|
-
try {
|
|
26791
|
-
nodes.push(validateAbstractionNode(await readJsonFile(filePath)));
|
|
26792
|
-
} catch (error) {
|
|
26793
|
-
invalidNodes.push({
|
|
26794
|
-
path: filePath,
|
|
26795
|
-
error: error instanceof Error ? error.message : String(error)
|
|
26796
|
-
});
|
|
26797
|
-
}
|
|
26798
|
-
}
|
|
26799
|
-
nodes.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
|
|
26800
|
-
const byKind = {};
|
|
26801
|
-
const byLevel = {};
|
|
26802
|
-
for (const node of nodes) {
|
|
26803
|
-
byKind[node.kind] = (byKind[node.kind] ?? 0) + 1;
|
|
26804
|
-
byLevel[node.abstractionLevel] = (byLevel[node.abstractionLevel] ?? 0) + 1;
|
|
26805
|
-
}
|
|
26806
|
-
return {
|
|
26807
|
-
enabled: options.enabled,
|
|
26808
|
-
anchorsEnabled: options.anchorsEnabled,
|
|
26809
|
-
rootDir,
|
|
26810
|
-
nodesDir,
|
|
26811
|
-
nodes: {
|
|
26812
|
-
total: files.length,
|
|
26813
|
-
valid: nodes.length,
|
|
26814
|
-
invalid: invalidNodes.length,
|
|
26815
|
-
byKind,
|
|
26816
|
-
byLevel,
|
|
26817
|
-
latestNodeId: nodes[0]?.nodeId,
|
|
26818
|
-
latestRecordedAt: nodes[0]?.recordedAt,
|
|
26819
|
-
latestSessionKey: nodes[0]?.sessionKey
|
|
26820
|
-
},
|
|
26821
|
-
latestNode: nodes[0],
|
|
26822
|
-
invalidNodes
|
|
26823
|
-
};
|
|
26824
|
-
}
|
|
26825
|
-
|
|
26826
|
-
// src/cue-anchors.ts
|
|
26827
|
-
import path55 from "path";
|
|
26828
|
-
import { mkdir as mkdir37, writeFile as writeFile34 } from "fs/promises";
|
|
26829
|
-
function validateAnchorType(raw) {
|
|
26830
|
-
const value = assertString2(raw, "anchorType");
|
|
26831
|
-
if (!["entity", "file", "tool", "outcome", "constraint", "date"].includes(value)) {
|
|
26832
|
-
throw new Error("anchorType must be one of entity|file|tool|outcome|constraint|date");
|
|
26833
|
-
}
|
|
26834
|
-
return value;
|
|
26835
|
-
}
|
|
26836
|
-
function validateNodeRefs(raw) {
|
|
26837
|
-
const nodeRefs = optionalStringArray2(raw, "nodeRefs");
|
|
26838
|
-
if (!nodeRefs || nodeRefs.length === 0) {
|
|
26839
|
-
throw new Error("nodeRefs must contain at least one node reference");
|
|
26840
|
-
}
|
|
26841
|
-
return nodeRefs.map((nodeRef, index) => assertSafePathSegment(nodeRef, `nodeRefs[${index}]`));
|
|
26842
|
-
}
|
|
26843
|
-
function resolveCueAnchorStoreDir(abstractionNodeStoreDir, overrideDir) {
|
|
26844
|
-
if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
|
|
26845
|
-
return overrideDir.trim();
|
|
26846
|
-
}
|
|
26847
|
-
return path55.join(abstractionNodeStoreDir, "anchors");
|
|
26848
|
-
}
|
|
26849
|
-
function validateCueAnchor(raw) {
|
|
26850
|
-
if (!isRecord2(raw)) throw new Error("cue anchor must be an object");
|
|
26851
|
-
if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
|
|
26852
|
-
return {
|
|
26853
|
-
schemaVersion: 1,
|
|
26854
|
-
anchorId: assertSafePathSegment(assertString2(raw.anchorId, "anchorId"), "anchorId"),
|
|
26855
|
-
anchorType: validateAnchorType(raw.anchorType),
|
|
26856
|
-
anchorValue: assertString2(raw.anchorValue, "anchorValue"),
|
|
26857
|
-
normalizedCue: assertString2(raw.normalizedCue, "normalizedCue"),
|
|
26858
|
-
recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
|
|
26859
|
-
sessionKey: assertString2(raw.sessionKey, "sessionKey"),
|
|
26860
|
-
nodeRefs: validateNodeRefs(raw.nodeRefs),
|
|
26861
|
-
tags: optionalStringArray2(raw.tags, "tags"),
|
|
26862
|
-
metadata: validateStringRecord(raw.metadata, "metadata")
|
|
26863
|
-
};
|
|
26864
|
-
}
|
|
26865
|
-
async function getCueAnchorStoreStatus(options) {
|
|
26866
|
-
const abstractionNodeStoreDir = options.abstractionNodeStoreDir?.trim().length ? options.abstractionNodeStoreDir.trim() : path55.join(options.memoryDir, "state", "abstraction-nodes");
|
|
26867
|
-
const rootDir = resolveCueAnchorStoreDir(abstractionNodeStoreDir, options.cueAnchorStoreDir);
|
|
26868
|
-
const files = await listJsonFiles(rootDir);
|
|
26869
|
-
const anchors = [];
|
|
26870
|
-
const invalidAnchors = [];
|
|
26871
|
-
for (const filePath of files) {
|
|
26872
|
-
try {
|
|
26873
|
-
anchors.push(validateCueAnchor(await readJsonFile(filePath)));
|
|
26874
|
-
} catch (error) {
|
|
26875
|
-
invalidAnchors.push({
|
|
26876
|
-
path: filePath,
|
|
26877
|
-
error: error instanceof Error ? error.message : String(error)
|
|
26878
|
-
});
|
|
26879
|
-
}
|
|
26880
|
-
}
|
|
26881
|
-
anchors.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
|
|
26882
|
-
const byType = {};
|
|
26883
|
-
let totalNodeRefs = 0;
|
|
26884
|
-
for (const anchor of anchors) {
|
|
26885
|
-
byType[anchor.anchorType] = (byType[anchor.anchorType] ?? 0) + 1;
|
|
26886
|
-
totalNodeRefs += anchor.nodeRefs.length;
|
|
26887
|
-
}
|
|
26888
|
-
return {
|
|
26889
|
-
enabled: options.enabled,
|
|
26890
|
-
anchorsEnabled: options.anchorsEnabled,
|
|
26891
|
-
rootDir,
|
|
26892
|
-
anchors: {
|
|
26893
|
-
total: files.length,
|
|
26894
|
-
valid: anchors.length,
|
|
26895
|
-
invalid: invalidAnchors.length,
|
|
26896
|
-
byType,
|
|
26897
|
-
totalNodeRefs,
|
|
26898
|
-
latestAnchorId: anchors[0]?.anchorId,
|
|
26899
|
-
latestRecordedAt: anchors[0]?.recordedAt,
|
|
26900
|
-
latestSessionKey: anchors[0]?.sessionKey
|
|
26901
|
-
},
|
|
26902
|
-
latestAnchor: anchors[0],
|
|
26903
|
-
invalidAnchors
|
|
26904
|
-
};
|
|
26905
|
-
}
|
|
26906
|
-
|
|
26907
27251
|
// src/cli.ts
|
|
26908
27252
|
function rankCandidateForKeep(a, b) {
|
|
26909
27253
|
const aConfidence = typeof a.frontmatter.confidence === "number" ? a.frontmatter.confidence : 0;
|
|
@@ -27129,6 +27473,26 @@ async function runCueAnchorStatusCliCommand(options) {
|
|
|
27129
27473
|
anchorsEnabled: options.abstractionAnchorsEnabled
|
|
27130
27474
|
});
|
|
27131
27475
|
}
|
|
27476
|
+
async function runHarmonicSearchCliCommand(options) {
|
|
27477
|
+
if (!options.harmonicRetrievalEnabled) return [];
|
|
27478
|
+
return searchHarmonicRetrieval({
|
|
27479
|
+
memoryDir: options.memoryDir,
|
|
27480
|
+
abstractionNodeStoreDir: options.abstractionNodeStoreDir,
|
|
27481
|
+
query: options.query,
|
|
27482
|
+
maxResults: Math.max(1, Math.floor(options.maxResults ?? 3)),
|
|
27483
|
+
sessionKey: options.sessionKey,
|
|
27484
|
+
anchorsEnabled: options.abstractionAnchorsEnabled
|
|
27485
|
+
});
|
|
27486
|
+
}
|
|
27487
|
+
async function runVerifiedRecallSearchCliCommand(options) {
|
|
27488
|
+
if (!options.verifiedRecallEnabled) return [];
|
|
27489
|
+
return searchVerifiedEpisodes({
|
|
27490
|
+
memoryDir: options.memoryDir,
|
|
27491
|
+
query: options.query,
|
|
27492
|
+
maxResults: Math.max(1, Math.floor(options.maxResults ?? 3)),
|
|
27493
|
+
boxRecallDays: options.boxRecallDays
|
|
27494
|
+
});
|
|
27495
|
+
}
|
|
27132
27496
|
async function runTrustZonePromoteCliCommand(options) {
|
|
27133
27497
|
const result = await promoteTrustZoneRecord({
|
|
27134
27498
|
memoryDir: options.memoryDir,
|
|
@@ -27375,7 +27739,7 @@ function policyVersionForValues(values, config) {
|
|
|
27375
27739
|
return createHash10("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
|
|
27376
27740
|
}
|
|
27377
27741
|
async function readRuntimePolicySnapshot2(config, fileName) {
|
|
27378
|
-
const filePath =
|
|
27742
|
+
const filePath = path57.join(config.memoryDir, "state", fileName);
|
|
27379
27743
|
const snapshot = await readRuntimePolicySnapshot(filePath, {
|
|
27380
27744
|
maxStaleDecayThreshold: config.lifecycleArchiveDecayThreshold
|
|
27381
27745
|
});
|
|
@@ -27959,14 +28323,14 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
|
|
|
27959
28323
|
const ns = (namespace ?? "").trim();
|
|
27960
28324
|
if (!ns) return orchestrator.config.memoryDir;
|
|
27961
28325
|
if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
|
|
27962
|
-
const candidate =
|
|
28326
|
+
const candidate = path57.join(orchestrator.config.memoryDir, "namespaces", ns);
|
|
27963
28327
|
if (ns === orchestrator.config.defaultNamespace) {
|
|
27964
28328
|
return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
|
|
27965
28329
|
}
|
|
27966
28330
|
return candidate;
|
|
27967
28331
|
}
|
|
27968
28332
|
async function readAllMemoryFiles(memoryDir) {
|
|
27969
|
-
const roots = [
|
|
28333
|
+
const roots = [path57.join(memoryDir, "facts"), path57.join(memoryDir, "corrections")];
|
|
27970
28334
|
const out = [];
|
|
27971
28335
|
const walk = async (dir) => {
|
|
27972
28336
|
let entries;
|
|
@@ -27977,7 +28341,7 @@ async function readAllMemoryFiles(memoryDir) {
|
|
|
27977
28341
|
}
|
|
27978
28342
|
for (const entry of entries) {
|
|
27979
28343
|
const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
|
|
27980
|
-
const fullPath =
|
|
28344
|
+
const fullPath = path57.join(dir, entryName);
|
|
27981
28345
|
if (entry.isDirectory()) {
|
|
27982
28346
|
await walk(fullPath);
|
|
27983
28347
|
continue;
|
|
@@ -28340,6 +28704,22 @@ function registerCli(api, orchestrator) {
|
|
|
28340
28704
|
console.log(JSON.stringify(status, null, 2));
|
|
28341
28705
|
console.log("OK");
|
|
28342
28706
|
});
|
|
28707
|
+
cmd.command("harmonic-search").description("Preview harmonic retrieval blending over abstraction nodes and cue anchors").argument("<query>", "Prompt-like query to evaluate against harmonic retrieval storage").option("--max-results <count>", "Maximum number of blended results to return", "3").option("--session-key <sessionKey>", "Optional session key for same-session tie-breaking").action(async (...args) => {
|
|
28708
|
+
const query = typeof args[0] === "string" ? args[0] : "";
|
|
28709
|
+
const options = args[1] ?? {};
|
|
28710
|
+
const maxResults = typeof options.maxResults === "string" ? Number.parseInt(options.maxResults, 10) : 3;
|
|
28711
|
+
const results = await runHarmonicSearchCliCommand({
|
|
28712
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
28713
|
+
abstractionNodeStoreDir: orchestrator.config.abstractionNodeStoreDir,
|
|
28714
|
+
harmonicRetrievalEnabled: orchestrator.config.harmonicRetrievalEnabled,
|
|
28715
|
+
abstractionAnchorsEnabled: orchestrator.config.abstractionAnchorsEnabled,
|
|
28716
|
+
query,
|
|
28717
|
+
maxResults: Number.isFinite(maxResults) ? maxResults : 3,
|
|
28718
|
+
sessionKey: typeof options.sessionKey === "string" ? options.sessionKey : void 0
|
|
28719
|
+
});
|
|
28720
|
+
console.log(JSON.stringify(results, null, 2));
|
|
28721
|
+
console.log("OK");
|
|
28722
|
+
});
|
|
28343
28723
|
cmd.command("trust-zone-promote").description("Dry-run or apply a trust-zone promotion with provenance enforcement").requiredOption("--record-id <recordId>", "Source trust-zone record id").requiredOption("--target-zone <targetZone>", "Promotion target zone (working|trusted)").requiredOption("--reason <reason>", "Human-readable promotion reason").option("--recorded-at <isoTimestamp>", "Promotion timestamp (defaults to now)").option("--summary <summary>", "Optional replacement summary for the promoted record").option("--dry-run", "Show the promotion plan without writing the promoted record").action(async (...args) => {
|
|
28344
28724
|
const options = args[0] ?? {};
|
|
28345
28725
|
const result = await runTrustZonePromoteCliCommand({
|
|
@@ -28358,6 +28738,20 @@ function registerCli(api, orchestrator) {
|
|
|
28358
28738
|
console.log(JSON.stringify(result, null, 2));
|
|
28359
28739
|
console.log("OK");
|
|
28360
28740
|
});
|
|
28741
|
+
cmd.command("verified-recall-search").description("Preview verified episodic recall over recent memory boxes").argument("<query>", "Prompt-like query to evaluate against verified episodic recall").option("--max-results <count>", "Maximum number of verified episodic results to return", "3").action(async (...args) => {
|
|
28742
|
+
const query = typeof args[0] === "string" ? args[0] : "";
|
|
28743
|
+
const options = args[1] ?? {};
|
|
28744
|
+
const maxResults = typeof options.maxResults === "string" ? Number.parseInt(options.maxResults, 10) : 3;
|
|
28745
|
+
const results = await runVerifiedRecallSearchCliCommand({
|
|
28746
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
28747
|
+
verifiedRecallEnabled: orchestrator.config.verifiedRecallEnabled,
|
|
28748
|
+
query,
|
|
28749
|
+
maxResults: Number.isFinite(maxResults) ? maxResults : 3,
|
|
28750
|
+
boxRecallDays: orchestrator.config.boxRecallDays
|
|
28751
|
+
});
|
|
28752
|
+
console.log(JSON.stringify(results, null, 2));
|
|
28753
|
+
console.log("OK");
|
|
28754
|
+
});
|
|
28361
28755
|
cmd.command("conversation-index-health").description("Show conversation index backend health and index stats").action(async () => {
|
|
28362
28756
|
const health = await runConversationIndexHealthCliCommand(orchestrator);
|
|
28363
28757
|
console.log(JSON.stringify(health, null, 2));
|
|
@@ -29007,7 +29401,7 @@ function registerCli(api, orchestrator) {
|
|
|
29007
29401
|
}
|
|
29008
29402
|
});
|
|
29009
29403
|
cmd.command("identity").description("Show agent identity reflections").action(async () => {
|
|
29010
|
-
const workspaceDir =
|
|
29404
|
+
const workspaceDir = path57.join(process.env.HOME ?? "~", ".openclaw", "workspace");
|
|
29011
29405
|
const identity = await orchestrator.storage.readIdentity(workspaceDir);
|
|
29012
29406
|
if (!identity) {
|
|
29013
29407
|
console.log("No identity file found.");
|
|
@@ -29230,8 +29624,8 @@ function registerCli(api, orchestrator) {
|
|
|
29230
29624
|
const options = args[0] ?? {};
|
|
29231
29625
|
const threadId = options.thread;
|
|
29232
29626
|
const top = parseInt(options.top ?? "10", 10);
|
|
29233
|
-
const memoryDir =
|
|
29234
|
-
const threading = new ThreadingManager(
|
|
29627
|
+
const memoryDir = path57.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
|
|
29628
|
+
const threading = new ThreadingManager(path57.join(memoryDir, "threads"));
|
|
29235
29629
|
if (threadId) {
|
|
29236
29630
|
const thread = await threading.loadThread(threadId);
|
|
29237
29631
|
if (!thread) {
|
|
@@ -29709,7 +30103,7 @@ async function recordObjectiveStateSnapshotsFromAgentMessages(options) {
|
|
|
29709
30103
|
// src/index.ts
|
|
29710
30104
|
import { readFile as readFile38, writeFile as writeFile35 } from "fs/promises";
|
|
29711
30105
|
import { readFileSync as readFileSync4 } from "fs";
|
|
29712
|
-
import
|
|
30106
|
+
import path58 from "path";
|
|
29713
30107
|
import os6 from "os";
|
|
29714
30108
|
var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
|
|
29715
30109
|
var ENGRAM_HOOK_APIS = "__openclawEngramHookApis";
|
|
@@ -29717,7 +30111,7 @@ function loadPluginConfigFromFile() {
|
|
|
29717
30111
|
try {
|
|
29718
30112
|
const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
|
|
29719
30113
|
const homeDir = process.env.HOME ?? os6.homedir();
|
|
29720
|
-
const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath :
|
|
30114
|
+
const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path58.join(homeDir, ".openclaw", "openclaw.json");
|
|
29721
30115
|
const content = readFileSync4(configPath, "utf-8");
|
|
29722
30116
|
const config = JSON.parse(content);
|
|
29723
30117
|
const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
|
|
@@ -29967,7 +30361,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
|
|
|
29967
30361
|
`session reset via API for ${sessionKey}, new sessionId=${result.sessionId}`
|
|
29968
30362
|
);
|
|
29969
30363
|
const safeSessionKey = sanitizeSessionKeyForFilename(sessionKey);
|
|
29970
|
-
const signalPath =
|
|
30364
|
+
const signalPath = path58.join(
|
|
29971
30365
|
workspaceDir,
|
|
29972
30366
|
`.compaction-reset-signal-${safeSessionKey}`
|
|
29973
30367
|
);
|
|
@@ -29998,7 +30392,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
|
|
|
29998
30392
|
);
|
|
29999
30393
|
async function ensureHourlySummaryCron(api2) {
|
|
30000
30394
|
const jobId = "engram-hourly-summary";
|
|
30001
|
-
const cronFilePath =
|
|
30395
|
+
const cronFilePath = path58.join(os6.homedir(), ".openclaw", "cron", "jobs.json");
|
|
30002
30396
|
try {
|
|
30003
30397
|
let jobsData = { version: 1, jobs: [] };
|
|
30004
30398
|
try {
|