@joshuaswarren/openclaw-engram 8.3.53 → 8.3.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +478 -225
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -425,7 +425,7 @@ function parseConfig(raw) {
|
|
|
425
425
|
}
|
|
426
426
|
|
|
427
427
|
// src/orchestrator.ts
|
|
428
|
-
import
|
|
428
|
+
import path25 from "path";
|
|
429
429
|
import { createHash as createHash5 } from "crypto";
|
|
430
430
|
import { mkdir as mkdir18, readFile as readFile18, writeFile as writeFile17 } from "fs/promises";
|
|
431
431
|
|
|
@@ -10927,6 +10927,18 @@ endTs: ${c.endTs}
|
|
|
10927
10927
|
}
|
|
10928
10928
|
return written;
|
|
10929
10929
|
}
|
|
10930
|
+
async function upsertConversationChunksFailOpen(adapter, chunks) {
|
|
10931
|
+
if (!adapter) {
|
|
10932
|
+
return { upserted: 0, skipped: true, reason: "adapter-unavailable" };
|
|
10933
|
+
}
|
|
10934
|
+
try {
|
|
10935
|
+
const upserted = await adapter.upsertChunks(chunks);
|
|
10936
|
+
return { upserted, skipped: false };
|
|
10937
|
+
} catch (err) {
|
|
10938
|
+
log.debug(`conversation index FAISS upsert failed (fail-open): ${err}`);
|
|
10939
|
+
return { upserted: 0, skipped: true, reason: "adapter-error" };
|
|
10940
|
+
}
|
|
10941
|
+
}
|
|
10930
10942
|
|
|
10931
10943
|
// src/conversation-index/cleanup.ts
|
|
10932
10944
|
import { readdir as readdir8, rm } from "fs/promises";
|
|
@@ -10962,8 +10974,212 @@ async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
|
10962
10974
|
}
|
|
10963
10975
|
}
|
|
10964
10976
|
|
|
10965
|
-
// src/
|
|
10977
|
+
// src/conversation-index/faiss-adapter.ts
|
|
10978
|
+
import * as childProcess from "child_process";
|
|
10979
|
+
import { fileURLToPath } from "url";
|
|
10966
10980
|
import path20 from "path";
|
|
10981
|
+
var FaissAdapterError = class extends Error {
|
|
10982
|
+
constructor(message, code) {
|
|
10983
|
+
super(message);
|
|
10984
|
+
this.code = code;
|
|
10985
|
+
this.name = "FaissAdapterError";
|
|
10986
|
+
}
|
|
10987
|
+
};
|
|
10988
|
+
function resolveDefaultFaissScriptPath(fromModuleUrl = import.meta.url) {
|
|
10989
|
+
const currentFile = fileURLToPath(fromModuleUrl);
|
|
10990
|
+
const moduleDir = path20.dirname(currentFile);
|
|
10991
|
+
if (moduleDir.endsWith(`${path20.sep}conversation-index`)) {
|
|
10992
|
+
return path20.resolve(moduleDir, "..", "..", "scripts", "faiss_index.py");
|
|
10993
|
+
}
|
|
10994
|
+
return path20.resolve(moduleDir, "..", "scripts", "faiss_index.py");
|
|
10995
|
+
}
|
|
10996
|
+
var FaissConversationIndexAdapter = class {
|
|
10997
|
+
constructor(config) {
|
|
10998
|
+
this.config = config;
|
|
10999
|
+
this.pythonBin = config.pythonBin && config.pythonBin.trim().length > 0 ? config.pythonBin.trim() : "python3";
|
|
11000
|
+
this.scriptPath = config.scriptPath && config.scriptPath.trim().length > 0 ? config.scriptPath.trim() : resolveDefaultFaissScriptPath();
|
|
11001
|
+
this.indexPath = path20.isAbsolute(config.indexDir) ? config.indexDir : path20.join(config.memoryDir, config.indexDir);
|
|
11002
|
+
this.spawnFn = config.spawnFn ?? childProcess.spawn;
|
|
11003
|
+
}
|
|
11004
|
+
pythonBin;
|
|
11005
|
+
scriptPath;
|
|
11006
|
+
indexPath;
|
|
11007
|
+
spawnFn;
|
|
11008
|
+
async upsertChunks(chunks) {
|
|
11009
|
+
if (this.config.maxBatchSize <= 0) return 0;
|
|
11010
|
+
let totalUpserted = 0;
|
|
11011
|
+
for (let offset = 0; offset < chunks.length; offset += this.config.maxBatchSize) {
|
|
11012
|
+
const batch = chunks.slice(offset, offset + this.config.maxBatchSize);
|
|
11013
|
+
if (batch.length === 0) continue;
|
|
11014
|
+
const payload = {
|
|
11015
|
+
modelId: this.config.modelId,
|
|
11016
|
+
indexPath: this.indexPath,
|
|
11017
|
+
chunks: batch.map((chunk) => ({
|
|
11018
|
+
id: chunk.id,
|
|
11019
|
+
sessionKey: chunk.sessionKey,
|
|
11020
|
+
text: chunk.text,
|
|
11021
|
+
startTs: chunk.startTs,
|
|
11022
|
+
endTs: chunk.endTs
|
|
11023
|
+
}))
|
|
11024
|
+
};
|
|
11025
|
+
const result = await this.runCommand("upsert", payload, this.config.upsertTimeoutMs);
|
|
11026
|
+
const upserted = result.upserted;
|
|
11027
|
+
if (typeof upserted !== "number" || !Number.isFinite(upserted)) {
|
|
11028
|
+
throw new FaissAdapterError("FAISS sidecar produced malformed upsert response", "malformed_output");
|
|
11029
|
+
}
|
|
11030
|
+
totalUpserted += Math.max(0, Math.floor(upserted));
|
|
11031
|
+
}
|
|
11032
|
+
return totalUpserted;
|
|
11033
|
+
}
|
|
11034
|
+
async searchChunks(query, topK) {
|
|
11035
|
+
const requestedTopK = Number.isFinite(topK) ? Math.floor(topK) : 0;
|
|
11036
|
+
const boundedTopK = this.config.maxSearchK > 0 ? Math.max(0, Math.min(requestedTopK, this.config.maxSearchK)) : 0;
|
|
11037
|
+
if (boundedTopK <= 0 || query.trim().length === 0) return [];
|
|
11038
|
+
const payload = {
|
|
11039
|
+
modelId: this.config.modelId,
|
|
11040
|
+
indexPath: this.indexPath,
|
|
11041
|
+
query,
|
|
11042
|
+
topK: boundedTopK
|
|
11043
|
+
};
|
|
11044
|
+
const result = await this.runCommand("search", payload, this.config.searchTimeoutMs);
|
|
11045
|
+
if (!Array.isArray(result.results)) {
|
|
11046
|
+
throw new FaissAdapterError("FAISS sidecar produced malformed search response", "malformed_output");
|
|
11047
|
+
}
|
|
11048
|
+
const rows = result.results;
|
|
11049
|
+
return rows.filter(
|
|
11050
|
+
(row) => row && typeof row.path === "string" && typeof row.snippet === "string" && typeof row.score === "number"
|
|
11051
|
+
).map((row) => ({ path: row.path, snippet: row.snippet, score: row.score }));
|
|
11052
|
+
}
|
|
11053
|
+
async health() {
|
|
11054
|
+
const payload = {
|
|
11055
|
+
modelId: this.config.modelId,
|
|
11056
|
+
indexPath: this.indexPath
|
|
11057
|
+
};
|
|
11058
|
+
const result = await this.runCommand("health", payload, this.config.healthTimeoutMs);
|
|
11059
|
+
if (result.status !== "ok" && result.status !== "degraded" && result.status !== "error") {
|
|
11060
|
+
throw new FaissAdapterError("FAISS sidecar produced malformed health response", "malformed_output");
|
|
11061
|
+
}
|
|
11062
|
+
return {
|
|
11063
|
+
ok: result.ok === true,
|
|
11064
|
+
status: result.status,
|
|
11065
|
+
indexPath: this.indexPath,
|
|
11066
|
+
message: typeof result.error === "string" && result.error.length > 0 ? result.error : void 0
|
|
11067
|
+
};
|
|
11068
|
+
}
|
|
11069
|
+
async runCommand(command, payload, timeoutMs) {
|
|
11070
|
+
const args = [this.scriptPath, command];
|
|
11071
|
+
const child = this.spawnFn(this.pythonBin, args, {
|
|
11072
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
11073
|
+
});
|
|
11074
|
+
const stdoutChunks = [];
|
|
11075
|
+
const stderrChunks = [];
|
|
11076
|
+
let timedOut = false;
|
|
11077
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
11078
|
+
timedOut = true;
|
|
11079
|
+
child.kill("SIGKILL");
|
|
11080
|
+
}, timeoutMs) : void 0;
|
|
11081
|
+
child.stdout.on("data", (chunk) => {
|
|
11082
|
+
stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
11083
|
+
});
|
|
11084
|
+
child.stderr.on("data", (chunk) => {
|
|
11085
|
+
stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
11086
|
+
});
|
|
11087
|
+
let code;
|
|
11088
|
+
try {
|
|
11089
|
+
child.stdin.write(JSON.stringify(payload));
|
|
11090
|
+
child.stdin.end();
|
|
11091
|
+
code = await new Promise((resolve, reject) => {
|
|
11092
|
+
const rejectAsProcessError = (err) => {
|
|
11093
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11094
|
+
reject(new FaissAdapterError(`FAISS sidecar stream/process error (${command}): ${msg}`, "non_zero_exit"));
|
|
11095
|
+
};
|
|
11096
|
+
child.once("error", rejectAsProcessError);
|
|
11097
|
+
child.stdin.once("error", rejectAsProcessError);
|
|
11098
|
+
child.once("close", (exitCode) => resolve(exitCode));
|
|
11099
|
+
});
|
|
11100
|
+
} catch (err) {
|
|
11101
|
+
if (err instanceof FaissAdapterError) throw err;
|
|
11102
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11103
|
+
throw new FaissAdapterError(`FAISS sidecar stream/process error (${command}): ${msg}`, "non_zero_exit");
|
|
11104
|
+
} finally {
|
|
11105
|
+
if (timer) clearTimeout(timer);
|
|
11106
|
+
}
|
|
11107
|
+
const stdout = Buffer.concat(stdoutChunks).toString("utf-8").trim();
|
|
11108
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
11109
|
+
if (timedOut) {
|
|
11110
|
+
throw new FaissAdapterError(
|
|
11111
|
+
`FAISS sidecar command timed out (${command}, ${timeoutMs}ms)`,
|
|
11112
|
+
"timeout"
|
|
11113
|
+
);
|
|
11114
|
+
}
|
|
11115
|
+
if (code !== 0) {
|
|
11116
|
+
throw new FaissAdapterError(
|
|
11117
|
+
`FAISS sidecar exited non-zero (${command}, code=${code ?? "null"})${stderr ? `: ${stderr}` : ""}`,
|
|
11118
|
+
"non_zero_exit"
|
|
11119
|
+
);
|
|
11120
|
+
}
|
|
11121
|
+
if (stdout.length === 0) {
|
|
11122
|
+
throw new FaissAdapterError(
|
|
11123
|
+
`FAISS sidecar produced empty output (${command})`,
|
|
11124
|
+
"malformed_output"
|
|
11125
|
+
);
|
|
11126
|
+
}
|
|
11127
|
+
let parsed;
|
|
11128
|
+
try {
|
|
11129
|
+
parsed = JSON.parse(stdout);
|
|
11130
|
+
} catch {
|
|
11131
|
+
throw new FaissAdapterError(
|
|
11132
|
+
`FAISS sidecar produced malformed JSON (${command})`,
|
|
11133
|
+
"malformed_output"
|
|
11134
|
+
);
|
|
11135
|
+
}
|
|
11136
|
+
if (parsed.ok === false) {
|
|
11137
|
+
const message = typeof parsed.error === "string" && parsed.error.length > 0 ? parsed.error : `FAISS sidecar command failed (${command})`;
|
|
11138
|
+
throw new FaissAdapterError(message, "non_zero_exit");
|
|
11139
|
+
}
|
|
11140
|
+
if (parsed.ok !== true) {
|
|
11141
|
+
throw new FaissAdapterError(
|
|
11142
|
+
`FAISS sidecar produced malformed success envelope (${command})`,
|
|
11143
|
+
"malformed_output"
|
|
11144
|
+
);
|
|
11145
|
+
}
|
|
11146
|
+
return parsed;
|
|
11147
|
+
}
|
|
11148
|
+
};
|
|
11149
|
+
async function failOpenFaissHealth(adapter) {
|
|
11150
|
+
if (!adapter) {
|
|
11151
|
+
return { ok: false, status: "error", indexPath: "", message: "adapter-unavailable" };
|
|
11152
|
+
}
|
|
11153
|
+
try {
|
|
11154
|
+
return await adapter.health();
|
|
11155
|
+
} catch (err) {
|
|
11156
|
+
log.debug(`faiss adapter health failed (fail-open): ${err}`);
|
|
11157
|
+
return { ok: false, status: "error", indexPath: "", message: "adapter-error" };
|
|
11158
|
+
}
|
|
11159
|
+
}
|
|
11160
|
+
|
|
11161
|
+
// src/conversation-index/search.ts
|
|
11162
|
+
async function searchConversationIndex(qmd, query, maxResults) {
|
|
11163
|
+
try {
|
|
11164
|
+
const results = await qmd.search(query, void 0, maxResults);
|
|
11165
|
+
return results.map((r) => ({ path: r.path, snippet: r.snippet, score: r.score }));
|
|
11166
|
+
} catch (err) {
|
|
11167
|
+
log.debug(`conversation index search failed: ${err}`);
|
|
11168
|
+
return [];
|
|
11169
|
+
}
|
|
11170
|
+
}
|
|
11171
|
+
async function searchConversationIndexFaissFailOpen(adapter, query, maxResults) {
|
|
11172
|
+
if (!adapter) return [];
|
|
11173
|
+
try {
|
|
11174
|
+
return await adapter.searchChunks(query, maxResults);
|
|
11175
|
+
} catch (err) {
|
|
11176
|
+
log.debug(`conversation index FAISS search failed (fail-open): ${err}`);
|
|
11177
|
+
return [];
|
|
11178
|
+
}
|
|
11179
|
+
}
|
|
11180
|
+
|
|
11181
|
+
// src/namespaces/storage.ts
|
|
11182
|
+
import path21 from "path";
|
|
10967
11183
|
import { access } from "fs/promises";
|
|
10968
11184
|
async function exists(p) {
|
|
10969
11185
|
try {
|
|
@@ -10985,7 +11201,7 @@ var NamespaceStorageRouter = class {
|
|
|
10985
11201
|
this.defaultNsRootResolved = this.config.memoryDir;
|
|
10986
11202
|
return this.defaultNsRootResolved;
|
|
10987
11203
|
}
|
|
10988
|
-
const nsDir =
|
|
11204
|
+
const nsDir = path21.join(this.config.memoryDir, "namespaces", this.config.defaultNamespace);
|
|
10989
11205
|
this.defaultNsRootResolved = await exists(nsDir) ? nsDir : this.config.memoryDir;
|
|
10990
11206
|
return this.defaultNsRootResolved;
|
|
10991
11207
|
}
|
|
@@ -10994,7 +11210,7 @@ var NamespaceStorageRouter = class {
|
|
|
10994
11210
|
if (namespace === this.config.defaultNamespace) {
|
|
10995
11211
|
return this.defaultNsRootResolved ?? this.config.memoryDir;
|
|
10996
11212
|
}
|
|
10997
|
-
return
|
|
11213
|
+
return path21.join(this.config.memoryDir, "namespaces", namespace);
|
|
10998
11214
|
}
|
|
10999
11215
|
async storageFor(namespace) {
|
|
11000
11216
|
const ns = namespace || this.config.defaultNamespace;
|
|
@@ -11071,7 +11287,7 @@ function recallNamespacesForPrincipal(principal, config) {
|
|
|
11071
11287
|
|
|
11072
11288
|
// src/shared-context/manager.ts
|
|
11073
11289
|
import { mkdir as mkdir15, readFile as readFile15, readdir as readdir9, appendFile as appendFile5, writeFile as writeFile14, stat as stat4 } from "fs/promises";
|
|
11074
|
-
import
|
|
11290
|
+
import path22 from "path";
|
|
11075
11291
|
import os3 from "os";
|
|
11076
11292
|
import { z as z3 } from "zod";
|
|
11077
11293
|
var SharedFeedbackEntrySchema = z3.object({
|
|
@@ -11093,15 +11309,15 @@ function ymd(d) {
|
|
|
11093
11309
|
var SharedContextManager = class {
|
|
11094
11310
|
constructor(config) {
|
|
11095
11311
|
this.config = config;
|
|
11096
|
-
const base = typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0 ? config.sharedContextDir :
|
|
11312
|
+
const base = typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0 ? config.sharedContextDir : path22.join(os3.homedir(), ".openclaw", "workspace", "shared-context");
|
|
11097
11313
|
this.dir = base;
|
|
11098
|
-
this.prioritiesPath =
|
|
11099
|
-
this.prioritiesInboxPath =
|
|
11100
|
-
this.outputsDir =
|
|
11101
|
-
this.roundtableDir =
|
|
11102
|
-
this.feedbackDir =
|
|
11103
|
-
this.feedbackInboxPath =
|
|
11104
|
-
this.crossSignalsDir =
|
|
11314
|
+
this.prioritiesPath = path22.join(base, "priorities.md");
|
|
11315
|
+
this.prioritiesInboxPath = path22.join(base, "priorities.inbox.md");
|
|
11316
|
+
this.outputsDir = path22.join(base, "agent-outputs");
|
|
11317
|
+
this.roundtableDir = path22.join(base, "roundtable");
|
|
11318
|
+
this.feedbackDir = path22.join(base, "feedback");
|
|
11319
|
+
this.feedbackInboxPath = path22.join(this.feedbackDir, "inbox.jsonl");
|
|
11320
|
+
this.crossSignalsDir = path22.join(base, "cross-signals");
|
|
11105
11321
|
}
|
|
11106
11322
|
dir;
|
|
11107
11323
|
prioritiesPath;
|
|
@@ -11117,10 +11333,10 @@ var SharedContextManager = class {
|
|
|
11117
11333
|
await mkdir15(this.roundtableDir, { recursive: true });
|
|
11118
11334
|
await mkdir15(this.feedbackDir, { recursive: true });
|
|
11119
11335
|
await mkdir15(this.crossSignalsDir, { recursive: true });
|
|
11120
|
-
await mkdir15(
|
|
11121
|
-
await mkdir15(
|
|
11122
|
-
await mkdir15(
|
|
11123
|
-
await mkdir15(
|
|
11336
|
+
await mkdir15(path22.join(this.dir, "staging"), { recursive: true });
|
|
11337
|
+
await mkdir15(path22.join(this.dir, "kpis"), { recursive: true });
|
|
11338
|
+
await mkdir15(path22.join(this.dir, "calendar"), { recursive: true });
|
|
11339
|
+
await mkdir15(path22.join(this.dir, "content-calendar"), { recursive: true });
|
|
11124
11340
|
await this.ensureFile(
|
|
11125
11341
|
this.prioritiesPath,
|
|
11126
11342
|
[
|
|
@@ -11164,7 +11380,7 @@ var SharedContextManager = class {
|
|
|
11164
11380
|
async readLatestRoundtable() {
|
|
11165
11381
|
try {
|
|
11166
11382
|
const files = (await readdir9(this.roundtableDir)).filter((f) => f.endsWith(".md")).sort().reverse();
|
|
11167
|
-
const fp = files[0] ?
|
|
11383
|
+
const fp = files[0] ? path22.join(this.roundtableDir, files[0]) : null;
|
|
11168
11384
|
if (!fp) return "";
|
|
11169
11385
|
return await readFile15(fp, "utf-8");
|
|
11170
11386
|
} catch {
|
|
@@ -11176,9 +11392,9 @@ var SharedContextManager = class {
|
|
|
11176
11392
|
const date = ymd(createdAt);
|
|
11177
11393
|
const time = createdAt.toISOString().slice(11, 19).replace(/:/g, "");
|
|
11178
11394
|
const slug = safeSlug(opts.title);
|
|
11179
|
-
const dir =
|
|
11395
|
+
const dir = path22.join(this.outputsDir, opts.agentId, date);
|
|
11180
11396
|
await mkdir15(dir, { recursive: true });
|
|
11181
|
-
const fp =
|
|
11397
|
+
const fp = path22.join(dir, `${time}-${slug}.md`);
|
|
11182
11398
|
const body = `---
|
|
11183
11399
|
kind: agent_output
|
|
11184
11400
|
agent: ${opts.agentId}
|
|
@@ -11213,11 +11429,11 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
|
|
|
11213
11429
|
const agents = await readdir9(this.outputsDir, { withFileTypes: true });
|
|
11214
11430
|
for (const a of agents) {
|
|
11215
11431
|
if (!a.isDirectory()) continue;
|
|
11216
|
-
const dayDir =
|
|
11432
|
+
const dayDir = path22.join(this.outputsDir, a.name, date);
|
|
11217
11433
|
try {
|
|
11218
11434
|
const files = (await readdir9(dayDir)).filter((f) => f.endsWith(".md")).sort();
|
|
11219
11435
|
for (const f of files) {
|
|
11220
|
-
const p =
|
|
11436
|
+
const p = path22.join(dayDir, f);
|
|
11221
11437
|
const raw = await readFile15(p, "utf-8");
|
|
11222
11438
|
const title = (raw.match(/^title:\s*(.+)$/m)?.[1] ?? f).trim();
|
|
11223
11439
|
outputs.push({ path: p, title });
|
|
@@ -11257,7 +11473,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
|
|
|
11257
11473
|
];
|
|
11258
11474
|
const out = md.join("\n");
|
|
11259
11475
|
const trimmed = out.length > maxChars ? out.slice(0, maxChars) + "\n\n...(trimmed)\n" : out;
|
|
11260
|
-
const fp =
|
|
11476
|
+
const fp = path22.join(this.roundtableDir, `${date}.md`);
|
|
11261
11477
|
await writeFile14(fp, trimmed, "utf-8");
|
|
11262
11478
|
log.info(`shared-context curated daily roundtable: ${fp}`);
|
|
11263
11479
|
return fp;
|
|
@@ -11266,7 +11482,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
|
|
|
11266
11482
|
|
|
11267
11483
|
// src/compounding/engine.ts
|
|
11268
11484
|
import { mkdir as mkdir16, readFile as readFile16, readdir as readdir10, writeFile as writeFile15 } from "fs/promises";
|
|
11269
|
-
import
|
|
11485
|
+
import path23 from "path";
|
|
11270
11486
|
import os4 from "os";
|
|
11271
11487
|
function isoWeekId(d) {
|
|
11272
11488
|
const dt = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
|
|
@@ -11297,7 +11513,7 @@ function sharedContextDir(config) {
|
|
|
11297
11513
|
if (typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0) {
|
|
11298
11514
|
return config.sharedContextDir;
|
|
11299
11515
|
}
|
|
11300
|
-
return
|
|
11516
|
+
return path23.join(os4.homedir(), ".openclaw", "workspace", "shared-context");
|
|
11301
11517
|
}
|
|
11302
11518
|
function cadenceStaleWindowMs(cadence) {
|
|
11303
11519
|
switch (cadence) {
|
|
@@ -11316,14 +11532,14 @@ function cadenceStaleWindowMs(cadence) {
|
|
|
11316
11532
|
var CompoundingEngine = class {
|
|
11317
11533
|
constructor(config) {
|
|
11318
11534
|
this.config = config;
|
|
11319
|
-
this.weeklyDir =
|
|
11320
|
-
this.mistakesPath =
|
|
11321
|
-
this.feedbackInboxPath =
|
|
11322
|
-
this.identityAnchorPath =
|
|
11323
|
-
this.identityIncidentsDir =
|
|
11324
|
-
this.identityAuditWeeklyDir =
|
|
11325
|
-
this.identityAuditMonthlyDir =
|
|
11326
|
-
this.identityImprovementLoopsPath =
|
|
11535
|
+
this.weeklyDir = path23.join(config.memoryDir, "compounding", "weekly");
|
|
11536
|
+
this.mistakesPath = path23.join(config.memoryDir, "compounding", "mistakes.json");
|
|
11537
|
+
this.feedbackInboxPath = path23.join(sharedContextDir(config), "feedback", "inbox.jsonl");
|
|
11538
|
+
this.identityAnchorPath = path23.join(config.memoryDir, "identity", "identity-anchor.md");
|
|
11539
|
+
this.identityIncidentsDir = path23.join(config.memoryDir, "identity", "incidents");
|
|
11540
|
+
this.identityAuditWeeklyDir = path23.join(config.memoryDir, "identity", "audits", "weekly");
|
|
11541
|
+
this.identityAuditMonthlyDir = path23.join(config.memoryDir, "identity", "audits", "monthly");
|
|
11542
|
+
this.identityImprovementLoopsPath = path23.join(config.memoryDir, "identity", "improvement-loops.md");
|
|
11327
11543
|
}
|
|
11328
11544
|
weeklyDir;
|
|
11329
11545
|
mistakesPath;
|
|
@@ -11335,7 +11551,7 @@ var CompoundingEngine = class {
|
|
|
11335
11551
|
identityImprovementLoopsPath;
|
|
11336
11552
|
async ensureDirs() {
|
|
11337
11553
|
await mkdir16(this.weeklyDir, { recursive: true });
|
|
11338
|
-
await mkdir16(
|
|
11554
|
+
await mkdir16(path23.dirname(this.mistakesPath), { recursive: true });
|
|
11339
11555
|
}
|
|
11340
11556
|
async synthesizeWeekly(opts) {
|
|
11341
11557
|
await this.ensureDirs();
|
|
@@ -11343,7 +11559,7 @@ var CompoundingEngine = class {
|
|
|
11343
11559
|
const entries = await this.readFeedbackEntriesForWeek(weekId);
|
|
11344
11560
|
const mistakes = this.buildMistakes(entries);
|
|
11345
11561
|
const continuity = this.config.continuityAuditEnabled ? await this.readContinuityAuditReferences(weekId) : { monthId: monthIdFromIsoWeek(weekId), weeklyPath: null, monthlyPath: null };
|
|
11346
|
-
const reportPath =
|
|
11562
|
+
const reportPath = path23.join(this.weeklyDir, `${weekId}.md`);
|
|
11347
11563
|
const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns, continuity);
|
|
11348
11564
|
await writeFile15(reportPath, md, "utf-8");
|
|
11349
11565
|
await writeFile15(this.mistakesPath, JSON.stringify(mistakes, null, 2) + "\n", "utf-8");
|
|
@@ -11423,7 +11639,7 @@ var CompoundingEngine = class {
|
|
|
11423
11639
|
];
|
|
11424
11640
|
const dir = period === "weekly" ? this.identityAuditWeeklyDir : this.identityAuditMonthlyDir;
|
|
11425
11641
|
await mkdir16(dir, { recursive: true });
|
|
11426
|
-
const reportPath =
|
|
11642
|
+
const reportPath = path23.join(dir, `${key}.md`);
|
|
11427
11643
|
await writeFile15(reportPath, lines.join("\n"), "utf-8");
|
|
11428
11644
|
return { period, key, reportPath };
|
|
11429
11645
|
}
|
|
@@ -11552,7 +11768,7 @@ var CompoundingEngine = class {
|
|
|
11552
11768
|
const files = names.filter((n) => n.endsWith(".md")).sort().reverse();
|
|
11553
11769
|
for (const file of files) {
|
|
11554
11770
|
if (incidents.length >= cappedLimit) break;
|
|
11555
|
-
const filePath =
|
|
11771
|
+
const filePath = path23.join(this.identityIncidentsDir, file);
|
|
11556
11772
|
try {
|
|
11557
11773
|
const raw = await readFile16(filePath, "utf-8");
|
|
11558
11774
|
const parsed = parseContinuityIncident(raw);
|
|
@@ -11568,8 +11784,8 @@ var CompoundingEngine = class {
|
|
|
11568
11784
|
}
|
|
11569
11785
|
async readContinuityAuditReferences(weekId) {
|
|
11570
11786
|
const monthId = monthIdFromIsoWeek(weekId);
|
|
11571
|
-
const weeklyPath =
|
|
11572
|
-
const monthlyPath =
|
|
11787
|
+
const weeklyPath = path23.join(this.identityAuditWeeklyDir, `${weekId}.md`);
|
|
11788
|
+
const monthlyPath = path23.join(this.identityAuditMonthlyDir, `${monthId}.md`);
|
|
11573
11789
|
const weeklyExists = await this.readNonEmptyFile(weeklyPath);
|
|
11574
11790
|
const monthlyExists = await this.readNonEmptyFile(monthlyPath);
|
|
11575
11791
|
return {
|
|
@@ -11685,7 +11901,7 @@ function selectRouteRule(text, rules, options) {
|
|
|
11685
11901
|
|
|
11686
11902
|
// src/routing/store.ts
|
|
11687
11903
|
import { lstat, mkdir as mkdir17, readFile as readFile17, realpath, rename as rename2, rm as rm2, stat as stat5, writeFile as writeFile16 } from "fs/promises";
|
|
11688
|
-
import
|
|
11904
|
+
import path24 from "path";
|
|
11689
11905
|
import { createHash as createHash4 } from "crypto";
|
|
11690
11906
|
function defaultState() {
|
|
11691
11907
|
return {
|
|
@@ -11704,14 +11920,14 @@ function stableRuleId(rule) {
|
|
|
11704
11920
|
return `route-${createHash4("sha256").update(seed).digest("hex").slice(0, 12)}`;
|
|
11705
11921
|
}
|
|
11706
11922
|
function resolveStatePath(memoryDir, stateFile) {
|
|
11707
|
-
const root =
|
|
11708
|
-
const defaultPath =
|
|
11709
|
-
if (
|
|
11710
|
-
const absolute =
|
|
11711
|
-
return absolute.startsWith(root +
|
|
11923
|
+
const root = path24.resolve(memoryDir);
|
|
11924
|
+
const defaultPath = path24.join(root, "state", "routing-rules.json");
|
|
11925
|
+
if (path24.isAbsolute(stateFile)) {
|
|
11926
|
+
const absolute = path24.resolve(stateFile);
|
|
11927
|
+
return absolute.startsWith(root + path24.sep) ? absolute : defaultPath;
|
|
11712
11928
|
}
|
|
11713
|
-
const resolved =
|
|
11714
|
-
return resolved.startsWith(root +
|
|
11929
|
+
const resolved = path24.resolve(root, stateFile);
|
|
11930
|
+
return resolved.startsWith(root + path24.sep) ? resolved : defaultPath;
|
|
11715
11931
|
}
|
|
11716
11932
|
function normalizeRule(rule, options) {
|
|
11717
11933
|
if (!rule || typeof rule !== "object") return null;
|
|
@@ -11744,7 +11960,7 @@ var RoutingRulesStore = class {
|
|
|
11744
11960
|
lockPath;
|
|
11745
11961
|
writeQueue = Promise.resolve();
|
|
11746
11962
|
constructor(memoryDir, stateFile = "state/routing-rules.json") {
|
|
11747
|
-
this.memoryRoot =
|
|
11963
|
+
this.memoryRoot = path24.resolve(memoryDir);
|
|
11748
11964
|
this.statePath = resolveStatePath(memoryDir, stateFile);
|
|
11749
11965
|
this.lockPath = `${this.statePath}.lock`;
|
|
11750
11966
|
}
|
|
@@ -11850,7 +12066,7 @@ var RoutingRulesStore = class {
|
|
|
11850
12066
|
const timeoutMs = 5e3;
|
|
11851
12067
|
let unexpectedLockError = null;
|
|
11852
12068
|
await this.assertStatePathScoped();
|
|
11853
|
-
await mkdir17(
|
|
12069
|
+
await mkdir17(path24.dirname(this.lockPath), { recursive: true });
|
|
11854
12070
|
while (Date.now() - start < timeoutMs) {
|
|
11855
12071
|
try {
|
|
11856
12072
|
await mkdir17(this.lockPath);
|
|
@@ -11885,12 +12101,12 @@ var RoutingRulesStore = class {
|
|
|
11885
12101
|
async assertStatePathScoped() {
|
|
11886
12102
|
await mkdir17(this.memoryRoot, { recursive: true });
|
|
11887
12103
|
const canonicalRoot = await realpath(this.memoryRoot);
|
|
11888
|
-
const canonicalParent = await this.canonicalizePathWithoutCreating(
|
|
11889
|
-
const canonicalStatePath =
|
|
12104
|
+
const canonicalParent = await this.canonicalizePathWithoutCreating(path24.dirname(this.statePath));
|
|
12105
|
+
const canonicalStatePath = path24.join(canonicalParent, path24.basename(this.statePath));
|
|
11890
12106
|
if (!this.isPathInside(canonicalRoot, canonicalStatePath)) {
|
|
11891
12107
|
throw new Error(`routing rules state path escaped memoryDir: ${canonicalStatePath}`);
|
|
11892
12108
|
}
|
|
11893
|
-
await mkdir17(
|
|
12109
|
+
await mkdir17(path24.dirname(this.statePath), { recursive: true });
|
|
11894
12110
|
try {
|
|
11895
12111
|
const stateStats = await lstat(this.statePath);
|
|
11896
12112
|
if (stateStats.isSymbolicLink()) {
|
|
@@ -11907,28 +12123,28 @@ var RoutingRulesStore = class {
|
|
|
11907
12123
|
}
|
|
11908
12124
|
}
|
|
11909
12125
|
isPathInside(root, candidate) {
|
|
11910
|
-
const normalizedRoot =
|
|
11911
|
-
const normalizedCandidate =
|
|
12126
|
+
const normalizedRoot = path24.resolve(root);
|
|
12127
|
+
const normalizedCandidate = path24.resolve(candidate);
|
|
11912
12128
|
if (normalizedCandidate === normalizedRoot) return true;
|
|
11913
|
-
if (normalizedRoot ===
|
|
12129
|
+
if (normalizedRoot === path24.parse(normalizedRoot).root) {
|
|
11914
12130
|
return normalizedCandidate.startsWith(normalizedRoot);
|
|
11915
12131
|
}
|
|
11916
|
-
return normalizedCandidate.startsWith(`${normalizedRoot}${
|
|
12132
|
+
return normalizedCandidate.startsWith(`${normalizedRoot}${path24.sep}`);
|
|
11917
12133
|
}
|
|
11918
12134
|
async canonicalizePathWithoutCreating(targetPath) {
|
|
11919
|
-
const absoluteTarget =
|
|
12135
|
+
const absoluteTarget = path24.resolve(targetPath);
|
|
11920
12136
|
let probe = absoluteTarget;
|
|
11921
12137
|
while (true) {
|
|
11922
12138
|
try {
|
|
11923
12139
|
const canonicalProbe = await realpath(probe);
|
|
11924
|
-
const remainder =
|
|
11925
|
-
return
|
|
12140
|
+
const remainder = path24.relative(probe, absoluteTarget);
|
|
12141
|
+
return path24.resolve(canonicalProbe, remainder);
|
|
11926
12142
|
} catch (err) {
|
|
11927
12143
|
const code = err.code;
|
|
11928
12144
|
if (code !== "ENOENT") {
|
|
11929
12145
|
throw err;
|
|
11930
12146
|
}
|
|
11931
|
-
const parent =
|
|
12147
|
+
const parent = path24.dirname(probe);
|
|
11932
12148
|
if (parent === probe) {
|
|
11933
12149
|
return absoluteTarget;
|
|
11934
12150
|
}
|
|
@@ -12117,11 +12333,11 @@ function mergeGraphExpandedResults(primary, expanded) {
|
|
|
12117
12333
|
return Array.from(mergedByPath.values());
|
|
12118
12334
|
}
|
|
12119
12335
|
function graphPathRelativeToStorage(storageDir, candidatePath) {
|
|
12120
|
-
const absolutePath =
|
|
12121
|
-
const rel =
|
|
12336
|
+
const absolutePath = path25.isAbsolute(candidatePath) ? candidatePath : path25.resolve(storageDir, candidatePath);
|
|
12337
|
+
const rel = path25.relative(storageDir, absolutePath);
|
|
12122
12338
|
if (!rel || rel === ".") return null;
|
|
12123
12339
|
if (rel.startsWith("..")) return null;
|
|
12124
|
-
return rel.split(
|
|
12340
|
+
return rel.split(path25.sep).join("/");
|
|
12125
12341
|
}
|
|
12126
12342
|
function graphActivationScoreToRecallScore(score) {
|
|
12127
12343
|
const bounded = Number.isFinite(score) && score > 0 ? score : 0;
|
|
@@ -12163,7 +12379,7 @@ function buildMemoryPathById(allMemsForGraph, storageDir) {
|
|
|
12163
12379
|
for (const mem of allMemsForGraph ?? []) {
|
|
12164
12380
|
const id = mem.frontmatter.id;
|
|
12165
12381
|
if (!id) continue;
|
|
12166
|
-
pathById.set(id,
|
|
12382
|
+
pathById.set(id, path25.relative(storageDir, mem.path));
|
|
12167
12383
|
}
|
|
12168
12384
|
return pathById;
|
|
12169
12385
|
}
|
|
@@ -12171,7 +12387,7 @@ function appendMemoryToGraphContext(options) {
|
|
|
12171
12387
|
if (!Array.isArray(options.allMemsForGraph)) return;
|
|
12172
12388
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
12173
12389
|
options.allMemsForGraph.push({
|
|
12174
|
-
path:
|
|
12390
|
+
path: path25.join(options.storageDir, options.memoryRelPath),
|
|
12175
12391
|
content: options.content,
|
|
12176
12392
|
frontmatter: {
|
|
12177
12393
|
id: options.memoryId,
|
|
@@ -12191,21 +12407,22 @@ function resolvePersistedMemoryRelativePath(options) {
|
|
|
12191
12407
|
const persisted = options.pathById.get(options.memoryId);
|
|
12192
12408
|
if (persisted) return persisted;
|
|
12193
12409
|
if (options.category === "correction") {
|
|
12194
|
-
return
|
|
12410
|
+
return path25.join("corrections", `${options.memoryId}.md`);
|
|
12195
12411
|
}
|
|
12196
12412
|
const idParts = options.memoryId.split("-");
|
|
12197
12413
|
const maybeTimestamp = Number(idParts[1]);
|
|
12198
12414
|
if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
|
|
12199
12415
|
const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
|
|
12200
|
-
return
|
|
12416
|
+
return path25.join("facts", day, `${options.memoryId}.md`);
|
|
12201
12417
|
}
|
|
12202
|
-
return
|
|
12418
|
+
return path25.join("facts", `${options.memoryId}.md`);
|
|
12203
12419
|
}
|
|
12204
12420
|
var Orchestrator = class _Orchestrator {
|
|
12205
12421
|
storage;
|
|
12206
12422
|
storageRouter;
|
|
12207
12423
|
qmd;
|
|
12208
12424
|
conversationQmd;
|
|
12425
|
+
conversationFaiss;
|
|
12209
12426
|
sharedContext;
|
|
12210
12427
|
compounding;
|
|
12211
12428
|
buffer;
|
|
@@ -12287,11 +12504,23 @@ var Orchestrator = class _Orchestrator {
|
|
|
12287
12504
|
daemonRecheckIntervalMs: config.qmdDaemonRecheckIntervalMs
|
|
12288
12505
|
}
|
|
12289
12506
|
) : void 0;
|
|
12507
|
+
this.conversationFaiss = config.conversationIndexEnabled && config.conversationIndexBackend === "faiss" ? new FaissConversationIndexAdapter({
|
|
12508
|
+
memoryDir: config.memoryDir,
|
|
12509
|
+
scriptPath: config.conversationIndexFaissScriptPath,
|
|
12510
|
+
pythonBin: config.conversationIndexFaissPythonBin,
|
|
12511
|
+
modelId: config.conversationIndexFaissModelId,
|
|
12512
|
+
indexDir: config.conversationIndexFaissIndexDir,
|
|
12513
|
+
upsertTimeoutMs: config.conversationIndexFaissUpsertTimeoutMs,
|
|
12514
|
+
searchTimeoutMs: config.conversationIndexFaissSearchTimeoutMs,
|
|
12515
|
+
healthTimeoutMs: config.conversationIndexFaissHealthTimeoutMs,
|
|
12516
|
+
maxBatchSize: config.conversationIndexFaissMaxBatchSize,
|
|
12517
|
+
maxSearchK: config.conversationIndexFaissMaxSearchK
|
|
12518
|
+
}) : void 0;
|
|
12290
12519
|
this.sharedContext = config.sharedContextEnabled ? new SharedContextManager(config) : void 0;
|
|
12291
12520
|
this.compounding = config.compoundingEnabled ? new CompoundingEngine(config) : void 0;
|
|
12292
12521
|
this.buffer = new SmartBuffer(config, this.storage);
|
|
12293
12522
|
this.transcript = new TranscriptManager(config);
|
|
12294
|
-
this.conversationIndexDir =
|
|
12523
|
+
this.conversationIndexDir = path25.join(config.memoryDir, "conversation-index", "chunks");
|
|
12295
12524
|
this.modelRegistry = new ModelRegistry(config.memoryDir);
|
|
12296
12525
|
this.relevance = new RelevanceStore(config.memoryDir);
|
|
12297
12526
|
this.negatives = new NegativeExampleStore(config.memoryDir);
|
|
@@ -12306,7 +12535,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
12306
12535
|
this.localLlm = new LocalLlmClient(config, this.modelRegistry);
|
|
12307
12536
|
this.extraction = new ExtractionEngine(config, this.localLlm, config.gatewayConfig, this.modelRegistry);
|
|
12308
12537
|
this.threading = new ThreadingManager(
|
|
12309
|
-
|
|
12538
|
+
path25.join(config.memoryDir, "threads"),
|
|
12310
12539
|
config.threadingGapMinutes
|
|
12311
12540
|
);
|
|
12312
12541
|
this.tmtBuilder = new TmtBuilder(config.memoryDir, {
|
|
@@ -12442,7 +12671,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
12442
12671
|
await this.lastRecall.load();
|
|
12443
12672
|
await this.sessionObserver.load();
|
|
12444
12673
|
if (this.config.factDeduplicationEnabled) {
|
|
12445
|
-
const stateDir2 =
|
|
12674
|
+
const stateDir2 = path25.join(this.config.memoryDir, "state");
|
|
12446
12675
|
this.contentHashIndex = new ContentHashIndex(stateDir2);
|
|
12447
12676
|
await this.contentHashIndex.load();
|
|
12448
12677
|
log.info(`content-hash dedup: loaded ${this.contentHashIndex.size} hashes`);
|
|
@@ -12475,12 +12704,12 @@ var Orchestrator = class _Orchestrator {
|
|
|
12475
12704
|
log.warn(`QMD: not available ${this.qmd.debugStatus()}`);
|
|
12476
12705
|
}
|
|
12477
12706
|
}
|
|
12478
|
-
if (this.config.conversationIndexEnabled && this.conversationQmd) {
|
|
12707
|
+
if (this.config.conversationIndexEnabled && this.config.conversationIndexBackend === "qmd" && this.conversationQmd) {
|
|
12479
12708
|
const available = await this.conversationQmd.probe();
|
|
12480
12709
|
if (available) {
|
|
12481
12710
|
log.info(`Conversation index QMD: available ${this.conversationQmd.debugStatus()}`);
|
|
12482
12711
|
const collectionState = await this.conversationQmd.ensureCollection(
|
|
12483
|
-
|
|
12712
|
+
path25.join(this.config.memoryDir, "conversation-index")
|
|
12484
12713
|
);
|
|
12485
12714
|
if (collectionState === "missing") {
|
|
12486
12715
|
this.config.conversationIndexEnabled = false;
|
|
@@ -12498,6 +12727,14 @@ var Orchestrator = class _Orchestrator {
|
|
|
12498
12727
|
log.warn(`Conversation index QMD: not available ${this.conversationQmd.debugStatus()}`);
|
|
12499
12728
|
}
|
|
12500
12729
|
}
|
|
12730
|
+
if (this.config.conversationIndexEnabled && this.config.conversationIndexBackend === "faiss" && this.conversationFaiss) {
|
|
12731
|
+
const health = await failOpenFaissHealth(this.conversationFaiss);
|
|
12732
|
+
if (health.ok) {
|
|
12733
|
+
log.info(`Conversation index FAISS: available (status=${health.status})`);
|
|
12734
|
+
} else {
|
|
12735
|
+
log.warn(`Conversation index FAISS: degraded (${health.message ?? health.status})`);
|
|
12736
|
+
}
|
|
12737
|
+
}
|
|
12501
12738
|
await this.buffer.load();
|
|
12502
12739
|
if (this.config.localLlmEnabled) {
|
|
12503
12740
|
await this.validateLocalLlmModel();
|
|
@@ -12516,12 +12753,12 @@ var Orchestrator = class _Orchestrator {
|
|
|
12516
12753
|
this.lastFileHygieneRunAtMs = now;
|
|
12517
12754
|
if (hygiene.rotateEnabled) {
|
|
12518
12755
|
for (const rel of hygiene.rotatePaths) {
|
|
12519
|
-
const abs =
|
|
12756
|
+
const abs = path25.isAbsolute(rel) ? rel : path25.join(this.config.workspaceDir, rel);
|
|
12520
12757
|
try {
|
|
12521
12758
|
const raw = await readFile18(abs, "utf-8");
|
|
12522
12759
|
if (raw.length > hygiene.rotateMaxBytes) {
|
|
12523
|
-
const archiveDir =
|
|
12524
|
-
const base =
|
|
12760
|
+
const archiveDir = path25.join(this.config.workspaceDir, hygiene.archiveDir);
|
|
12761
|
+
const base = path25.basename(abs);
|
|
12525
12762
|
const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
|
|
12526
12763
|
const { newContent } = await rotateMarkdownFileToArchive({
|
|
12527
12764
|
filePath: abs,
|
|
@@ -12546,8 +12783,8 @@ var Orchestrator = class _Orchestrator {
|
|
|
12546
12783
|
log.warn(w.message);
|
|
12547
12784
|
}
|
|
12548
12785
|
if (hygiene.warningsLogEnabled && warnings.length > 0) {
|
|
12549
|
-
const fp =
|
|
12550
|
-
await mkdir18(
|
|
12786
|
+
const fp = path25.join(this.config.memoryDir, hygiene.warningsLogPath);
|
|
12787
|
+
await mkdir18(path25.dirname(fp), { recursive: true });
|
|
12551
12788
|
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
12552
12789
|
const block = `
|
|
12553
12790
|
|
|
@@ -12615,7 +12852,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
12615
12852
|
}
|
|
12616
12853
|
async getLastGraphRecallSnapshot(namespace) {
|
|
12617
12854
|
const storage = await this.getStorage(namespace);
|
|
12618
|
-
const snapshotPath =
|
|
12855
|
+
const snapshotPath = path25.join(storage.dir, "state", "last_graph_recall.json");
|
|
12619
12856
|
try {
|
|
12620
12857
|
const raw = await readFile18(snapshotPath, "utf-8");
|
|
12621
12858
|
const parsed = JSON.parse(raw);
|
|
@@ -12655,6 +12892,32 @@ var Orchestrator = class _Orchestrator {
|
|
|
12655
12892
|
...expanded.map((e) => `- ${e.path} (score=${e.score.toFixed(3)}, ns=${e.namespace})`)
|
|
12656
12893
|
].join("\n");
|
|
12657
12894
|
}
|
|
12895
|
+
async searchConversationRecallResults(retrievalQuery, topK) {
|
|
12896
|
+
if (this.config.conversationIndexBackend === "faiss") {
|
|
12897
|
+
return searchConversationIndexFaissFailOpen(this.conversationFaiss, retrievalQuery, topK);
|
|
12898
|
+
}
|
|
12899
|
+
if (this.conversationQmd && this.conversationQmd.isAvailable()) {
|
|
12900
|
+
return searchConversationIndex(this.conversationQmd, retrievalQuery, topK);
|
|
12901
|
+
}
|
|
12902
|
+
return [];
|
|
12903
|
+
}
|
|
12904
|
+
formatConversationRecallSection(results, maxChars) {
|
|
12905
|
+
if (!Array.isArray(results) || results.length === 0) return null;
|
|
12906
|
+
const lines = ["## Semantic Recall (Past Conversations)", ""];
|
|
12907
|
+
let used = 0;
|
|
12908
|
+
for (const r of results) {
|
|
12909
|
+
if (!r?.snippet) continue;
|
|
12910
|
+
const chunk = `### ${r.path}
|
|
12911
|
+
Score: ${r.score.toFixed(3)}
|
|
12912
|
+
|
|
12913
|
+
${r.snippet.trim()}
|
|
12914
|
+
`;
|
|
12915
|
+
if (used + chunk.length > maxChars) break;
|
|
12916
|
+
lines.push(chunk);
|
|
12917
|
+
used += chunk.length;
|
|
12918
|
+
}
|
|
12919
|
+
return used > 0 ? lines.join("\n") : null;
|
|
12920
|
+
}
|
|
12658
12921
|
async updateConversationIndex(sessionKey, hours = 24, opts) {
|
|
12659
12922
|
if (!this.config.conversationIndexEnabled) {
|
|
12660
12923
|
return { chunks: 0, skipped: true, reason: "disabled", embedded: false };
|
|
@@ -12685,15 +12948,19 @@ var Orchestrator = class _Orchestrator {
|
|
|
12685
12948
|
this.conversationIndexDir,
|
|
12686
12949
|
this.config.conversationIndexRetentionDays
|
|
12687
12950
|
);
|
|
12688
|
-
const q = this.conversationQmd ?? this.qmd;
|
|
12689
|
-
const usingPrimaryQmdClient = q === this.qmd;
|
|
12690
12951
|
const shouldEmbed = opts?.embed ?? this.config.conversationIndexEmbedOnUpdate;
|
|
12691
12952
|
let embedded = false;
|
|
12692
|
-
if (
|
|
12693
|
-
await
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
12953
|
+
if (this.config.conversationIndexBackend === "faiss") {
|
|
12954
|
+
await upsertConversationChunksFailOpen(this.conversationFaiss, chunks);
|
|
12955
|
+
} else {
|
|
12956
|
+
const q = this.conversationQmd ?? this.qmd;
|
|
12957
|
+
const usingPrimaryQmdClient = q === this.qmd;
|
|
12958
|
+
if ((!usingPrimaryQmdClient || this.config.qmdEnabled) && q.isAvailable()) {
|
|
12959
|
+
await q.update();
|
|
12960
|
+
if (shouldEmbed) {
|
|
12961
|
+
await q.embed();
|
|
12962
|
+
embedded = true;
|
|
12963
|
+
}
|
|
12697
12964
|
}
|
|
12698
12965
|
}
|
|
12699
12966
|
this.conversationIndexLastUpdateAtMs.set(sessionKey, Date.now());
|
|
@@ -12917,7 +13184,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
12917
13184
|
const storage = await this.storageRouter.storageFor(namespace);
|
|
12918
13185
|
const seedRelativePaths = nsResults.slice(0, perNamespaceSeedCap).map((result) => graphPathRelativeToStorage(storage.dir, result.path)).filter((value) => typeof value === "string" && value.length > 0);
|
|
12919
13186
|
if (seedRelativePaths.length === 0) continue;
|
|
12920
|
-
seedPaths.push(...seedRelativePaths.map((rel) =>
|
|
13187
|
+
seedPaths.push(...seedRelativePaths.map((rel) => path25.join(storage.dir, rel)));
|
|
12921
13188
|
const seedSet = new Set(seedRelativePaths);
|
|
12922
13189
|
const expanded = await this.graphIndexFor(storage).spreadingActivation(
|
|
12923
13190
|
seedRelativePaths,
|
|
@@ -12926,7 +13193,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
12926
13193
|
if (expanded.length === 0) continue;
|
|
12927
13194
|
for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
|
|
12928
13195
|
if (seedSet.has(candidate.path)) continue;
|
|
12929
|
-
const memoryPath =
|
|
13196
|
+
const memoryPath = path25.resolve(storage.dir, candidate.path);
|
|
12930
13197
|
const memory = await storage.readMemoryByPath(memoryPath);
|
|
12931
13198
|
if (!memory) continue;
|
|
12932
13199
|
if (isArtifactMemoryPath(memory.path)) continue;
|
|
@@ -12954,8 +13221,8 @@ var Orchestrator = class _Orchestrator {
|
|
|
12954
13221
|
}
|
|
12955
13222
|
async recordLastGraphRecallSnapshot(options) {
|
|
12956
13223
|
try {
|
|
12957
|
-
const snapshotPath =
|
|
12958
|
-
await mkdir18(
|
|
13224
|
+
const snapshotPath = path25.join(options.storage.dir, "state", "last_graph_recall.json");
|
|
13225
|
+
await mkdir18(path25.dirname(snapshotPath), { recursive: true });
|
|
12959
13226
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12960
13227
|
const payload = {
|
|
12961
13228
|
recordedAt: now,
|
|
@@ -13539,36 +13806,22 @@ ${formatted}`);
|
|
|
13539
13806
|
}
|
|
13540
13807
|
timings.summaries = `${Date.now() - summariesT0}ms`;
|
|
13541
13808
|
const convT0 = Date.now();
|
|
13542
|
-
if (this.config.conversationIndexEnabled && !queryPolicy.skipConversationRecall
|
|
13809
|
+
if (this.config.conversationIndexEnabled && !queryPolicy.skipConversationRecall) {
|
|
13543
13810
|
const startedAtMs = Date.now();
|
|
13544
13811
|
const timeoutMs = Math.max(200, this.config.conversationRecallTimeoutMs);
|
|
13545
13812
|
const topK = Math.max(1, this.config.conversationRecallTopK);
|
|
13546
13813
|
const maxChars = Math.max(400, this.config.conversationRecallMaxChars);
|
|
13547
13814
|
const results = await Promise.race([
|
|
13548
|
-
this.
|
|
13815
|
+
this.searchConversationRecallResults(retrievalQuery, topK),
|
|
13549
13816
|
new Promise((resolve) => setTimeout(() => resolve([]), timeoutMs))
|
|
13550
13817
|
]).catch(() => []);
|
|
13551
13818
|
const durationMs = Date.now() - startedAtMs;
|
|
13552
13819
|
if (durationMs >= timeoutMs) {
|
|
13553
13820
|
log.debug(`conversation recall: timed out after ${timeoutMs}ms`);
|
|
13554
13821
|
}
|
|
13555
|
-
|
|
13556
|
-
|
|
13557
|
-
|
|
13558
|
-
for (const r of results) {
|
|
13559
|
-
if (!r?.snippet) continue;
|
|
13560
|
-
const chunk = `### ${r.path}
|
|
13561
|
-
Score: ${r.score.toFixed(3)}
|
|
13562
|
-
|
|
13563
|
-
${r.snippet.trim()}
|
|
13564
|
-
`;
|
|
13565
|
-
if (used + chunk.length > maxChars) break;
|
|
13566
|
-
lines.push(chunk);
|
|
13567
|
-
used += chunk.length;
|
|
13568
|
-
}
|
|
13569
|
-
if (used > 0) {
|
|
13570
|
-
sections.push(lines.join("\n"));
|
|
13571
|
-
}
|
|
13822
|
+
const formattedConversationRecall = this.formatConversationRecallSection(results, maxChars);
|
|
13823
|
+
if (formattedConversationRecall) {
|
|
13824
|
+
sections.push(formattedConversationRecall);
|
|
13572
13825
|
}
|
|
13573
13826
|
}
|
|
13574
13827
|
timings.convRecall = `${Date.now() - convT0}ms`;
|
|
@@ -14370,7 +14623,7 @@ _Context: ${topQuestion.context}_`);
|
|
|
14370
14623
|
const allMems = allMemsForGraph ?? [];
|
|
14371
14624
|
for (const m of allMems) {
|
|
14372
14625
|
if (m.frontmatter.entityRef === entityRef) {
|
|
14373
|
-
const rel =
|
|
14626
|
+
const rel = path25.relative(storage.dir, m.path);
|
|
14374
14627
|
if (rel !== memoryRelPath) entitySiblings.push(rel);
|
|
14375
14628
|
}
|
|
14376
14629
|
}
|
|
@@ -14747,8 +15000,8 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
|
|
|
14747
15000
|
protectedCategories: this.config.lifecycleProtectedCategories
|
|
14748
15001
|
}
|
|
14749
15002
|
};
|
|
14750
|
-
const metricsPath =
|
|
14751
|
-
await mkdir18(
|
|
15003
|
+
const metricsPath = path25.join(this.storage.dir, "state", "lifecycle-metrics.json");
|
|
15004
|
+
await mkdir18(path25.dirname(metricsPath), { recursive: true });
|
|
14752
15005
|
await writeFile17(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
|
|
14753
15006
|
}
|
|
14754
15007
|
/**
|
|
@@ -15015,7 +15268,7 @@ ${lines.join("\n\n")}`;
|
|
|
15015
15268
|
if (hits.length === 0) return [];
|
|
15016
15269
|
const results = [];
|
|
15017
15270
|
for (const hit of hits) {
|
|
15018
|
-
const fullPath =
|
|
15271
|
+
const fullPath = path25.isAbsolute(hit.path) ? hit.path : path25.join(this.config.memoryDir, hit.path);
|
|
15019
15272
|
const memory = await this.storage.readMemoryByPath(fullPath);
|
|
15020
15273
|
if (!memory) continue;
|
|
15021
15274
|
results.push({
|
|
@@ -15454,8 +15707,8 @@ ${lines.join("\n\n")}`;
|
|
|
15454
15707
|
}
|
|
15455
15708
|
namespaceFromStorageDir(storageDir) {
|
|
15456
15709
|
if (!this.config.namespacesEnabled) return this.config.defaultNamespace;
|
|
15457
|
-
const resolvedStorageDir =
|
|
15458
|
-
const resolvedMemoryDir =
|
|
15710
|
+
const resolvedStorageDir = path25.resolve(storageDir);
|
|
15711
|
+
const resolvedMemoryDir = path25.resolve(this.config.memoryDir);
|
|
15459
15712
|
if (resolvedStorageDir === resolvedMemoryDir) return this.config.defaultNamespace;
|
|
15460
15713
|
const m = resolvedStorageDir.match(/[\\/]namespaces[\\/]([^\\/]+)$/);
|
|
15461
15714
|
return m && m[1] ? m[1] : this.config.defaultNamespace;
|
|
@@ -15483,12 +15736,12 @@ ${lines.join("\n\n")}`;
|
|
|
15483
15736
|
};
|
|
15484
15737
|
|
|
15485
15738
|
// src/tools.ts
|
|
15486
|
-
import
|
|
15739
|
+
import path27 from "path";
|
|
15487
15740
|
import { createHash as createHash6 } from "crypto";
|
|
15488
15741
|
import { Type } from "@sinclair/typebox";
|
|
15489
15742
|
|
|
15490
15743
|
// src/work/storage.ts
|
|
15491
|
-
import
|
|
15744
|
+
import path26 from "path";
|
|
15492
15745
|
import { randomUUID } from "crypto";
|
|
15493
15746
|
import { mkdir as mkdir19, readdir as readdir11, readFile as readFile19, rm as rm3, writeFile as writeFile18 } from "fs/promises";
|
|
15494
15747
|
var TASK_TRANSITIONS = {
|
|
@@ -15565,8 +15818,8 @@ function ensureProjectStatus(value) {
|
|
|
15565
15818
|
var WorkStorage = class {
|
|
15566
15819
|
constructor(memoryDir) {
|
|
15567
15820
|
this.memoryDir = memoryDir;
|
|
15568
|
-
this.tasksDir =
|
|
15569
|
-
this.projectsDir =
|
|
15821
|
+
this.tasksDir = path26.join(memoryDir, "work", "tasks");
|
|
15822
|
+
this.projectsDir = path26.join(memoryDir, "work", "projects");
|
|
15570
15823
|
}
|
|
15571
15824
|
tasksDir;
|
|
15572
15825
|
projectsDir;
|
|
@@ -15576,11 +15829,11 @@ var WorkStorage = class {
|
|
|
15576
15829
|
}
|
|
15577
15830
|
taskPath(id) {
|
|
15578
15831
|
assertValidWorkId(id, "task");
|
|
15579
|
-
return
|
|
15832
|
+
return path26.join(this.tasksDir, `${id}.md`);
|
|
15580
15833
|
}
|
|
15581
15834
|
projectPath(id) {
|
|
15582
15835
|
assertValidWorkId(id, "project");
|
|
15583
|
-
return
|
|
15836
|
+
return path26.join(this.projectsDir, `${id}.md`);
|
|
15584
15837
|
}
|
|
15585
15838
|
serializeTask(task) {
|
|
15586
15839
|
return `${serializeFrontmatter2(task)}
|
|
@@ -15672,7 +15925,7 @@ ${project.description}
|
|
|
15672
15925
|
const out = [];
|
|
15673
15926
|
for (const entry of entries) {
|
|
15674
15927
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
15675
|
-
const raw = await readFile19(
|
|
15928
|
+
const raw = await readFile19(path26.join(this.tasksDir, entry.name), "utf-8");
|
|
15676
15929
|
const task = this.parseTask(raw);
|
|
15677
15930
|
if (!task) continue;
|
|
15678
15931
|
if (filter?.status && task.status !== filter.status) continue;
|
|
@@ -15771,7 +16024,7 @@ ${project.description}
|
|
|
15771
16024
|
const out = [];
|
|
15772
16025
|
for (const entry of entries) {
|
|
15773
16026
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
15774
|
-
const raw = await readFile19(
|
|
16027
|
+
const raw = await readFile19(path26.join(this.projectsDir, entry.name), "utf-8");
|
|
15775
16028
|
const project = this.parseProject(raw);
|
|
15776
16029
|
if (project) out.push(project);
|
|
15777
16030
|
}
|
|
@@ -17272,7 +17525,7 @@ Best for:
|
|
|
17272
17525
|
- Reviewing identity development over time`,
|
|
17273
17526
|
parameters: Type.Object({}),
|
|
17274
17527
|
async execute() {
|
|
17275
|
-
const workspaceDir =
|
|
17528
|
+
const workspaceDir = path27.join(process.env.HOME ?? "~", ".openclaw", "workspace");
|
|
17276
17529
|
const identity = await orchestrator.storage.readIdentity(workspaceDir);
|
|
17277
17530
|
if (!identity) {
|
|
17278
17531
|
return toolResult("No identity file found. Identity reflections build automatically through conversations when identityEnabled is true.");
|
|
@@ -17781,11 +18034,11 @@ mistakes: ${res.mistakesCount} patterns`
|
|
|
17781
18034
|
}
|
|
17782
18035
|
|
|
17783
18036
|
// src/cli.ts
|
|
17784
|
-
import
|
|
18037
|
+
import path43 from "path";
|
|
17785
18038
|
import { access as access3, readFile as readFile30, readdir as readdir18, unlink as unlink5 } from "fs/promises";
|
|
17786
18039
|
|
|
17787
18040
|
// src/transfer/export-json.ts
|
|
17788
|
-
import
|
|
18041
|
+
import path29 from "path";
|
|
17789
18042
|
import { mkdir as mkdir21, readFile as readFile21 } from "fs/promises";
|
|
17790
18043
|
|
|
17791
18044
|
// src/transfer/constants.ts
|
|
@@ -17795,7 +18048,7 @@ var EXPORT_SCHEMA_VERSION = 1;
|
|
|
17795
18048
|
// src/transfer/fs-utils.ts
|
|
17796
18049
|
import { createHash as createHash7 } from "crypto";
|
|
17797
18050
|
import { mkdir as mkdir20, readdir as readdir12, readFile as readFile20, stat as stat6, writeFile as writeFile19 } from "fs/promises";
|
|
17798
|
-
import
|
|
18051
|
+
import path28 from "path";
|
|
17799
18052
|
async function sha256File(filePath) {
|
|
17800
18053
|
const buf = await readFile20(filePath);
|
|
17801
18054
|
const sha256 = createHash7("sha256").update(buf).digest("hex");
|
|
@@ -17807,7 +18060,7 @@ function sha256String(content) {
|
|
|
17807
18060
|
return { sha256, bytes: buf.byteLength };
|
|
17808
18061
|
}
|
|
17809
18062
|
async function writeJsonFile(filePath, value) {
|
|
17810
|
-
await mkdir20(
|
|
18063
|
+
await mkdir20(path28.dirname(filePath), { recursive: true });
|
|
17811
18064
|
await writeFile19(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
17812
18065
|
}
|
|
17813
18066
|
async function readJsonFile(filePath) {
|
|
@@ -17819,7 +18072,7 @@ async function listFilesRecursive(rootDir) {
|
|
|
17819
18072
|
async function walk(dir) {
|
|
17820
18073
|
const entries = await readdir12(dir, { withFileTypes: true });
|
|
17821
18074
|
for (const ent of entries) {
|
|
17822
|
-
const fp =
|
|
18075
|
+
const fp = path28.join(dir, ent.name);
|
|
17823
18076
|
if (ent.isDirectory()) {
|
|
17824
18077
|
await walk(fp);
|
|
17825
18078
|
} else if (ent.isFile()) {
|
|
@@ -17839,11 +18092,11 @@ async function fileExists(filePath) {
|
|
|
17839
18092
|
}
|
|
17840
18093
|
}
|
|
17841
18094
|
function toPosixRelPath(absPath, rootDir) {
|
|
17842
|
-
const rel =
|
|
17843
|
-
return rel.split(
|
|
18095
|
+
const rel = path28.relative(rootDir, absPath);
|
|
18096
|
+
return rel.split(path28.sep).join("/");
|
|
17844
18097
|
}
|
|
17845
18098
|
function fromPosixRelPath(relPath) {
|
|
17846
|
-
return relPath.split("/").join(
|
|
18099
|
+
return relPath.split("/").join(path28.sep);
|
|
17847
18100
|
}
|
|
17848
18101
|
|
|
17849
18102
|
// src/transfer/export-json.ts
|
|
@@ -17859,9 +18112,9 @@ function shouldExclude(relPosix, includeTranscripts) {
|
|
|
17859
18112
|
}
|
|
17860
18113
|
async function exportJsonBundle(opts) {
|
|
17861
18114
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
17862
|
-
const outDirAbs =
|
|
18115
|
+
const outDirAbs = path29.resolve(opts.outDir);
|
|
17863
18116
|
await mkdir21(outDirAbs, { recursive: true });
|
|
17864
|
-
const memoryDirAbs =
|
|
18117
|
+
const memoryDirAbs = path29.resolve(opts.memoryDir);
|
|
17865
18118
|
const filesAbs = await listFilesRecursive(memoryDirAbs);
|
|
17866
18119
|
const records = [];
|
|
17867
18120
|
const manifestFiles = [];
|
|
@@ -17874,7 +18127,7 @@ async function exportJsonBundle(opts) {
|
|
|
17874
18127
|
manifestFiles.push({ path: relPosix, sha256, bytes });
|
|
17875
18128
|
}
|
|
17876
18129
|
if (opts.includeWorkspaceIdentity !== false && opts.workspaceDir) {
|
|
17877
|
-
const identityPath =
|
|
18130
|
+
const identityPath = path29.join(opts.workspaceDir, "IDENTITY.md");
|
|
17878
18131
|
try {
|
|
17879
18132
|
const content = await readFile21(identityPath, "utf-8");
|
|
17880
18133
|
const relPath = "workspace/IDENTITY.md";
|
|
@@ -17893,12 +18146,12 @@ async function exportJsonBundle(opts) {
|
|
|
17893
18146
|
files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
|
|
17894
18147
|
};
|
|
17895
18148
|
const bundle = { manifest, records };
|
|
17896
|
-
await writeJsonFile(
|
|
17897
|
-
await writeJsonFile(
|
|
18149
|
+
await writeJsonFile(path29.join(outDirAbs, "manifest.json"), manifest);
|
|
18150
|
+
await writeJsonFile(path29.join(outDirAbs, "bundle.json"), bundle);
|
|
17898
18151
|
}
|
|
17899
18152
|
|
|
17900
18153
|
// src/transfer/export-md.ts
|
|
17901
|
-
import
|
|
18154
|
+
import path30 from "path";
|
|
17902
18155
|
import { mkdir as mkdir22, readFile as readFile22, writeFile as writeFile20 } from "fs/promises";
|
|
17903
18156
|
function shouldExclude2(relPosix, includeTranscripts) {
|
|
17904
18157
|
const parts = relPosix.split("/");
|
|
@@ -17907,16 +18160,16 @@ function shouldExclude2(relPosix, includeTranscripts) {
|
|
|
17907
18160
|
}
|
|
17908
18161
|
async function exportMarkdownBundle(opts) {
|
|
17909
18162
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
17910
|
-
const outDirAbs =
|
|
18163
|
+
const outDirAbs = path30.resolve(opts.outDir);
|
|
17911
18164
|
await mkdir22(outDirAbs, { recursive: true });
|
|
17912
|
-
const memDirAbs =
|
|
18165
|
+
const memDirAbs = path30.resolve(opts.memoryDir);
|
|
17913
18166
|
const filesAbs = await listFilesRecursive(memDirAbs);
|
|
17914
18167
|
const manifestFiles = [];
|
|
17915
18168
|
for (const abs of filesAbs) {
|
|
17916
18169
|
const relPosix = toPosixRelPath(abs, memDirAbs);
|
|
17917
18170
|
if (shouldExclude2(relPosix, includeTranscripts)) continue;
|
|
17918
|
-
const dstAbs =
|
|
17919
|
-
await mkdir22(
|
|
18171
|
+
const dstAbs = path30.join(outDirAbs, ...relPosix.split("/"));
|
|
18172
|
+
await mkdir22(path30.dirname(dstAbs), { recursive: true });
|
|
17920
18173
|
const content = await readFile22(abs);
|
|
17921
18174
|
await writeFile20(dstAbs, content);
|
|
17922
18175
|
const { sha256, bytes } = await sha256File(abs);
|
|
@@ -17930,12 +18183,12 @@ async function exportMarkdownBundle(opts) {
|
|
|
17930
18183
|
includesTranscripts: includeTranscripts,
|
|
17931
18184
|
files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
|
|
17932
18185
|
};
|
|
17933
|
-
await writeJsonFile(
|
|
18186
|
+
await writeJsonFile(path30.join(outDirAbs, "manifest.json"), manifest);
|
|
17934
18187
|
}
|
|
17935
18188
|
async function looksLikeEngramMdExport(fromDir) {
|
|
17936
|
-
const dirAbs =
|
|
18189
|
+
const dirAbs = path30.resolve(fromDir);
|
|
17937
18190
|
try {
|
|
17938
|
-
const raw = await readFile22(
|
|
18191
|
+
const raw = await readFile22(path30.join(dirAbs, "manifest.json"), "utf-8");
|
|
17939
18192
|
const parsed = JSON.parse(raw);
|
|
17940
18193
|
return parsed.format === EXPORT_FORMAT && parsed.schemaVersion === EXPORT_SCHEMA_VERSION;
|
|
17941
18194
|
} catch {
|
|
@@ -17944,16 +18197,16 @@ async function looksLikeEngramMdExport(fromDir) {
|
|
|
17944
18197
|
}
|
|
17945
18198
|
|
|
17946
18199
|
// src/transfer/backup.ts
|
|
17947
|
-
import
|
|
18200
|
+
import path31 from "path";
|
|
17948
18201
|
import { mkdir as mkdir23, readdir as readdir13, rm as rm4 } from "fs/promises";
|
|
17949
18202
|
function timestampDirName(now) {
|
|
17950
18203
|
return now.toISOString().replace(/[:.]/g, "-");
|
|
17951
18204
|
}
|
|
17952
18205
|
async function backupMemoryDir(opts) {
|
|
17953
|
-
const outDirAbs =
|
|
18206
|
+
const outDirAbs = path31.resolve(opts.outDir);
|
|
17954
18207
|
await mkdir23(outDirAbs, { recursive: true });
|
|
17955
18208
|
const ts = timestampDirName(/* @__PURE__ */ new Date());
|
|
17956
|
-
const backupDir =
|
|
18209
|
+
const backupDir = path31.join(outDirAbs, ts);
|
|
17957
18210
|
await exportMarkdownBundle({
|
|
17958
18211
|
memoryDir: opts.memoryDir,
|
|
17959
18212
|
outDir: backupDir,
|
|
@@ -17978,13 +18231,13 @@ async function enforceRetention(outDirAbs, retentionDays) {
|
|
|
17978
18231
|
const tsMs = iso ? Date.parse(iso) : NaN;
|
|
17979
18232
|
if (!Number.isFinite(tsMs)) continue;
|
|
17980
18233
|
if (tsMs < cutoffMs) {
|
|
17981
|
-
await rm4(
|
|
18234
|
+
await rm4(path31.join(outDirAbs, name), { recursive: true, force: true });
|
|
17982
18235
|
}
|
|
17983
18236
|
}
|
|
17984
18237
|
}
|
|
17985
18238
|
|
|
17986
18239
|
// src/transfer/export-sqlite.ts
|
|
17987
|
-
import
|
|
18240
|
+
import path32 from "path";
|
|
17988
18241
|
import Database from "better-sqlite3";
|
|
17989
18242
|
import { readFile as readFile23 } from "fs/promises";
|
|
17990
18243
|
|
|
@@ -18012,8 +18265,8 @@ function shouldExclude3(relPosix, includeTranscripts) {
|
|
|
18012
18265
|
}
|
|
18013
18266
|
async function exportSqlite(opts) {
|
|
18014
18267
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
18015
|
-
const memDirAbs =
|
|
18016
|
-
const outAbs =
|
|
18268
|
+
const memDirAbs = path32.resolve(opts.memoryDir);
|
|
18269
|
+
const outAbs = path32.resolve(opts.outFile);
|
|
18017
18270
|
const filesAbs = await listFilesRecursive(memDirAbs);
|
|
18018
18271
|
const db = new Database(outAbs);
|
|
18019
18272
|
try {
|
|
@@ -18045,7 +18298,7 @@ async function exportSqlite(opts) {
|
|
|
18045
18298
|
}
|
|
18046
18299
|
|
|
18047
18300
|
// src/transfer/import-json.ts
|
|
18048
|
-
import
|
|
18301
|
+
import path33 from "path";
|
|
18049
18302
|
import { mkdir as mkdir24, writeFile as writeFile21 } from "fs/promises";
|
|
18050
18303
|
|
|
18051
18304
|
// src/transfer/types.ts
|
|
@@ -18079,21 +18332,21 @@ function normalizeForDedupe(s) {
|
|
|
18079
18332
|
}
|
|
18080
18333
|
async function importJsonBundle(opts) {
|
|
18081
18334
|
const conflict = opts.conflict ?? "skip";
|
|
18082
|
-
const fromDirAbs =
|
|
18083
|
-
const bundlePath =
|
|
18335
|
+
const fromDirAbs = path33.resolve(opts.fromDir);
|
|
18336
|
+
const bundlePath = path33.join(fromDirAbs, "bundle.json");
|
|
18084
18337
|
const bundle = ExportBundleV1Schema.parse(await readJsonFile(bundlePath));
|
|
18085
|
-
const memDirAbs =
|
|
18338
|
+
const memDirAbs = path33.resolve(opts.targetMemoryDir);
|
|
18086
18339
|
const written = [];
|
|
18087
18340
|
let skipped = 0;
|
|
18088
18341
|
for (const rec of bundle.records) {
|
|
18089
18342
|
const isWorkspace = rec.path.startsWith("workspace/");
|
|
18090
|
-
const targetBase = isWorkspace ? opts.workspaceDir ?
|
|
18343
|
+
const targetBase = isWorkspace ? opts.workspaceDir ? path33.resolve(opts.workspaceDir) : null : memDirAbs;
|
|
18091
18344
|
if (isWorkspace && !targetBase) {
|
|
18092
18345
|
skipped += 1;
|
|
18093
18346
|
continue;
|
|
18094
18347
|
}
|
|
18095
18348
|
const relFs = fromPosixRelPath(isWorkspace ? rec.path.replace(/^workspace\//, "") : rec.path);
|
|
18096
|
-
const absTarget =
|
|
18349
|
+
const absTarget = path33.join(targetBase, relFs);
|
|
18097
18350
|
const exists3 = await fileExists(absTarget);
|
|
18098
18351
|
if (exists3) {
|
|
18099
18352
|
if (conflict === "skip") {
|
|
@@ -18117,21 +18370,21 @@ async function importJsonBundle(opts) {
|
|
|
18117
18370
|
return { written: 0, skipped };
|
|
18118
18371
|
}
|
|
18119
18372
|
for (const w of written) {
|
|
18120
|
-
await mkdir24(
|
|
18373
|
+
await mkdir24(path33.dirname(w.abs), { recursive: true });
|
|
18121
18374
|
await writeFile21(w.abs, w.content, "utf-8");
|
|
18122
18375
|
}
|
|
18123
18376
|
return { written: written.length, skipped };
|
|
18124
18377
|
}
|
|
18125
18378
|
function looksLikeEngramJsonExport(fromDir) {
|
|
18126
|
-
const dir =
|
|
18379
|
+
const dir = path33.resolve(fromDir);
|
|
18127
18380
|
return Promise.all([
|
|
18128
|
-
fileExists(
|
|
18129
|
-
fileExists(
|
|
18381
|
+
fileExists(path33.join(dir, "manifest.json")),
|
|
18382
|
+
fileExists(path33.join(dir, "bundle.json"))
|
|
18130
18383
|
]).then(([m, b]) => m && b);
|
|
18131
18384
|
}
|
|
18132
18385
|
|
|
18133
18386
|
// src/transfer/import-sqlite.ts
|
|
18134
|
-
import
|
|
18387
|
+
import path34 from "path";
|
|
18135
18388
|
import Database2 from "better-sqlite3";
|
|
18136
18389
|
import { mkdir as mkdir25, writeFile as writeFile22 } from "fs/promises";
|
|
18137
18390
|
function normalizeForDedupe2(s) {
|
|
@@ -18139,8 +18392,8 @@ function normalizeForDedupe2(s) {
|
|
|
18139
18392
|
}
|
|
18140
18393
|
async function importSqlite(opts) {
|
|
18141
18394
|
const conflict = opts.conflict ?? "skip";
|
|
18142
|
-
const memDirAbs =
|
|
18143
|
-
const fromAbs =
|
|
18395
|
+
const memDirAbs = path34.resolve(opts.targetMemoryDir);
|
|
18396
|
+
const fromAbs = path34.resolve(opts.fromFile);
|
|
18144
18397
|
const db = new Database2(fromAbs, { readonly: true });
|
|
18145
18398
|
const written = [];
|
|
18146
18399
|
let skipped = 0;
|
|
@@ -18153,7 +18406,7 @@ async function importSqlite(opts) {
|
|
|
18153
18406
|
const rows = db.prepare("SELECT path_rel, content FROM files").all();
|
|
18154
18407
|
for (const r of rows) {
|
|
18155
18408
|
const relFs = fromPosixRelPath(r.path_rel);
|
|
18156
|
-
const absTarget =
|
|
18409
|
+
const absTarget = path34.join(memDirAbs, relFs);
|
|
18157
18410
|
const exists3 = await fileExists(absTarget);
|
|
18158
18411
|
if (exists3) {
|
|
18159
18412
|
if (conflict === "skip") {
|
|
@@ -18178,29 +18431,29 @@ async function importSqlite(opts) {
|
|
|
18178
18431
|
}
|
|
18179
18432
|
if (opts.dryRun) return { written: 0, skipped };
|
|
18180
18433
|
for (const w of written) {
|
|
18181
|
-
await mkdir25(
|
|
18434
|
+
await mkdir25(path34.dirname(w.abs), { recursive: true });
|
|
18182
18435
|
await writeFile22(w.abs, w.content, "utf-8");
|
|
18183
18436
|
}
|
|
18184
18437
|
return { written: written.length, skipped };
|
|
18185
18438
|
}
|
|
18186
18439
|
|
|
18187
18440
|
// src/transfer/import-md.ts
|
|
18188
|
-
import
|
|
18441
|
+
import path35 from "path";
|
|
18189
18442
|
import { mkdir as mkdir26, readFile as readFile24, writeFile as writeFile23 } from "fs/promises";
|
|
18190
18443
|
function normalizeForDedupe3(s) {
|
|
18191
18444
|
return s.replace(/\s+/g, " ").trim();
|
|
18192
18445
|
}
|
|
18193
18446
|
async function importMarkdownBundle(opts) {
|
|
18194
18447
|
const conflict = opts.conflict ?? "skip";
|
|
18195
|
-
const fromAbs =
|
|
18196
|
-
const targetAbs =
|
|
18448
|
+
const fromAbs = path35.resolve(opts.fromDir);
|
|
18449
|
+
const targetAbs = path35.resolve(opts.targetMemoryDir);
|
|
18197
18450
|
const filesAbs = await listFilesRecursive(fromAbs);
|
|
18198
18451
|
const writes = [];
|
|
18199
18452
|
let skipped = 0;
|
|
18200
18453
|
for (const abs of filesAbs) {
|
|
18201
18454
|
const relPosix = toPosixRelPath(abs, fromAbs);
|
|
18202
18455
|
if (relPosix === "manifest.json") continue;
|
|
18203
|
-
const dstAbs =
|
|
18456
|
+
const dstAbs = path35.join(targetAbs, fromPosixRelPath(relPosix));
|
|
18204
18457
|
const content = await readFile24(abs, "utf-8");
|
|
18205
18458
|
const exists3 = await fileExists(dstAbs);
|
|
18206
18459
|
if (exists3) {
|
|
@@ -18223,17 +18476,17 @@ async function importMarkdownBundle(opts) {
|
|
|
18223
18476
|
}
|
|
18224
18477
|
if (opts.dryRun) return { written: 0, skipped };
|
|
18225
18478
|
for (const w of writes) {
|
|
18226
|
-
await mkdir26(
|
|
18479
|
+
await mkdir26(path35.dirname(w.abs), { recursive: true });
|
|
18227
18480
|
await writeFile23(w.abs, w.content, "utf-8");
|
|
18228
18481
|
}
|
|
18229
18482
|
return { written: writes.length, skipped };
|
|
18230
18483
|
}
|
|
18231
18484
|
|
|
18232
18485
|
// src/transfer/autodetect.ts
|
|
18233
|
-
import
|
|
18486
|
+
import path36 from "path";
|
|
18234
18487
|
import { stat as stat7 } from "fs/promises";
|
|
18235
18488
|
async function detectImportFormat(fromPath) {
|
|
18236
|
-
const abs =
|
|
18489
|
+
const abs = path36.resolve(fromPath);
|
|
18237
18490
|
let st;
|
|
18238
18491
|
try {
|
|
18239
18492
|
st = await stat7(abs);
|
|
@@ -18661,8 +18914,8 @@ function gatherCandidates(input, warnings) {
|
|
|
18661
18914
|
const record = rec;
|
|
18662
18915
|
const content = typeof record.content === "string" ? record.content : null;
|
|
18663
18916
|
if (!content) continue;
|
|
18664
|
-
const
|
|
18665
|
-
if (!
|
|
18917
|
+
const path45 = typeof record.path === "string" ? record.path : "";
|
|
18918
|
+
if (!path45.startsWith("transcripts/") && !path45.includes("/transcripts/")) continue;
|
|
18666
18919
|
rows.push(...parseJsonl(content, warnings));
|
|
18667
18920
|
}
|
|
18668
18921
|
return rows;
|
|
@@ -18718,7 +18971,7 @@ var openclawReplayNormalizer = {
|
|
|
18718
18971
|
};
|
|
18719
18972
|
|
|
18720
18973
|
// src/maintenance/archive-observations.ts
|
|
18721
|
-
import
|
|
18974
|
+
import path37 from "path";
|
|
18722
18975
|
import { mkdir as mkdir27, readdir as readdir14, readFile as readFile25, unlink as unlink4, writeFile as writeFile24 } from "fs/promises";
|
|
18723
18976
|
var DATE_FILE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})\.(jsonl|md)$/;
|
|
18724
18977
|
function normalizeRetentionDays(value) {
|
|
@@ -18745,8 +18998,8 @@ async function listFilesRecursive2(root, relPrefix = "") {
|
|
|
18745
18998
|
return out;
|
|
18746
18999
|
}
|
|
18747
19000
|
for (const entry of entries) {
|
|
18748
|
-
const rel = relPrefix ?
|
|
18749
|
-
const full =
|
|
19001
|
+
const rel = relPrefix ? path37.join(relPrefix, entry.name) : entry.name;
|
|
19002
|
+
const full = path37.join(root, entry.name);
|
|
18750
19003
|
if (entry.isDirectory()) {
|
|
18751
19004
|
out.push(...await listFilesRecursive2(full, rel));
|
|
18752
19005
|
continue;
|
|
@@ -18756,19 +19009,19 @@ async function listFilesRecursive2(root, relPrefix = "") {
|
|
|
18756
19009
|
return out;
|
|
18757
19010
|
}
|
|
18758
19011
|
async function collectArchiveCandidates(memoryDir, cutoffTimeMs) {
|
|
18759
|
-
const roots = ["transcripts",
|
|
19012
|
+
const roots = ["transcripts", path37.join("state", "tool-usage"), path37.join("summaries", "hourly")];
|
|
18760
19013
|
const out = [];
|
|
18761
19014
|
for (const relRoot of roots) {
|
|
18762
|
-
const absRoot =
|
|
19015
|
+
const absRoot = path37.join(memoryDir, relRoot);
|
|
18763
19016
|
const files = await listFilesRecursive2(absRoot);
|
|
18764
19017
|
for (const fileRel of files) {
|
|
18765
|
-
const filename =
|
|
19018
|
+
const filename = path37.basename(fileRel);
|
|
18766
19019
|
const parsedDate = extractDateFromFilename(filename);
|
|
18767
19020
|
if (!parsedDate) continue;
|
|
18768
19021
|
if (parsedDate.getTime() >= cutoffTimeMs) continue;
|
|
18769
19022
|
out.push({
|
|
18770
|
-
absolutePath:
|
|
18771
|
-
relativePath:
|
|
19023
|
+
absolutePath: path37.join(absRoot, fileRel),
|
|
19024
|
+
relativePath: path37.join(relRoot, fileRel)
|
|
18772
19025
|
});
|
|
18773
19026
|
}
|
|
18774
19027
|
}
|
|
@@ -18783,7 +19036,7 @@ async function archiveObservations(options) {
|
|
|
18783
19036
|
new Date(now.getTime() - retentionDays * 24 * 60 * 60 * 1e3)
|
|
18784
19037
|
);
|
|
18785
19038
|
const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
18786
|
-
const archiveRoot =
|
|
19039
|
+
const archiveRoot = path37.join(options.memoryDir, "archive", "observations", stamp);
|
|
18787
19040
|
const candidates = retentionDays === 0 ? [] : await collectArchiveCandidates(
|
|
18788
19041
|
options.memoryDir,
|
|
18789
19042
|
cutoffDayStartUtc
|
|
@@ -18794,8 +19047,8 @@ async function archiveObservations(options) {
|
|
|
18794
19047
|
if (!dryRun && candidates.length > 0) {
|
|
18795
19048
|
await mkdir27(archiveRoot, { recursive: true });
|
|
18796
19049
|
for (const candidate of candidates) {
|
|
18797
|
-
const archivePath =
|
|
18798
|
-
const archiveDir =
|
|
19050
|
+
const archivePath = path37.join(archiveRoot, candidate.relativePath);
|
|
19051
|
+
const archiveDir = path37.dirname(archivePath);
|
|
18799
19052
|
await mkdir27(archiveDir, { recursive: true });
|
|
18800
19053
|
const raw = await readFile25(candidate.absolutePath);
|
|
18801
19054
|
await writeFile24(archivePath, raw);
|
|
@@ -18819,11 +19072,11 @@ async function archiveObservations(options) {
|
|
|
18819
19072
|
}
|
|
18820
19073
|
|
|
18821
19074
|
// src/maintenance/rebuild-observations.ts
|
|
18822
|
-
import
|
|
19075
|
+
import path39 from "path";
|
|
18823
19076
|
import { readdir as readdir15, readFile as readFile27 } from "fs/promises";
|
|
18824
19077
|
|
|
18825
19078
|
// src/maintenance/observation-ledger-utils.ts
|
|
18826
|
-
import
|
|
19079
|
+
import path38 from "path";
|
|
18827
19080
|
import { mkdir as mkdir28, readFile as readFile26, writeFile as writeFile25 } from "fs/promises";
|
|
18828
19081
|
function toHourBucketIso(timestamp) {
|
|
18829
19082
|
const normalized = /(?:Z|[+-]\d{2}:\d{2})$/u.test(timestamp) ? timestamp : `${timestamp}Z`;
|
|
@@ -18835,8 +19088,8 @@ function toHourBucketIso(timestamp) {
|
|
|
18835
19088
|
}
|
|
18836
19089
|
async function backupAndWriteRebuiltObservations(options) {
|
|
18837
19090
|
const stamp = options.now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
18838
|
-
const archiveRoot =
|
|
18839
|
-
let backupPath =
|
|
19091
|
+
const archiveRoot = path38.join(options.memoryDir, "archive", "observations", stamp);
|
|
19092
|
+
let backupPath = path38.join(
|
|
18840
19093
|
archiveRoot,
|
|
18841
19094
|
"state",
|
|
18842
19095
|
"observation-ledger",
|
|
@@ -18844,7 +19097,7 @@ async function backupAndWriteRebuiltObservations(options) {
|
|
|
18844
19097
|
);
|
|
18845
19098
|
try {
|
|
18846
19099
|
const existing = await readFile26(options.outputPath, "utf-8");
|
|
18847
|
-
await mkdir28(
|
|
19100
|
+
await mkdir28(path38.dirname(backupPath), { recursive: true });
|
|
18848
19101
|
await writeFile25(backupPath, existing, "utf-8");
|
|
18849
19102
|
} catch (err) {
|
|
18850
19103
|
const code = err.code;
|
|
@@ -18861,7 +19114,7 @@ async function backupAndWriteRebuiltObservations(options) {
|
|
|
18861
19114
|
rebuiltAt
|
|
18862
19115
|
})
|
|
18863
19116
|
);
|
|
18864
|
-
await mkdir28(
|
|
19117
|
+
await mkdir28(path38.dirname(options.outputPath), { recursive: true });
|
|
18865
19118
|
await writeFile25(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
|
|
18866
19119
|
` : "", "utf-8");
|
|
18867
19120
|
return backupPath;
|
|
@@ -18884,7 +19137,7 @@ async function listTranscriptFiles(root) {
|
|
|
18884
19137
|
for (const entry of entries) {
|
|
18885
19138
|
if (entry.name === "." || entry.name === "..") continue;
|
|
18886
19139
|
if (entry.isSymbolicLink()) continue;
|
|
18887
|
-
const full =
|
|
19140
|
+
const full = path39.join(root, entry.name);
|
|
18888
19141
|
if (entry.isDirectory()) {
|
|
18889
19142
|
out.push(...await listTranscriptFiles(full));
|
|
18890
19143
|
continue;
|
|
@@ -18944,8 +19197,8 @@ function buildLedgerRows(linesByFile) {
|
|
|
18944
19197
|
async function rebuildObservations(options) {
|
|
18945
19198
|
const dryRun = options.dryRun !== false;
|
|
18946
19199
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
18947
|
-
const transcriptsRoot =
|
|
18948
|
-
const outputPath =
|
|
19200
|
+
const transcriptsRoot = path39.join(options.memoryDir, "transcripts");
|
|
19201
|
+
const outputPath = path39.join(
|
|
18949
19202
|
options.memoryDir,
|
|
18950
19203
|
"state",
|
|
18951
19204
|
"observation-ledger",
|
|
@@ -18981,7 +19234,7 @@ async function rebuildObservations(options) {
|
|
|
18981
19234
|
}
|
|
18982
19235
|
|
|
18983
19236
|
// src/maintenance/migrate-observations.ts
|
|
18984
|
-
import
|
|
19237
|
+
import path40 from "path";
|
|
18985
19238
|
import { readdir as readdir16, readFile as readFile28 } from "fs/promises";
|
|
18986
19239
|
function toNonNegativeInt(value) {
|
|
18987
19240
|
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
@@ -19045,15 +19298,15 @@ async function listLegacyObservationFiles(root) {
|
|
|
19045
19298
|
async function migrateObservations(options) {
|
|
19046
19299
|
const dryRun = options.dryRun !== false;
|
|
19047
19300
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
19048
|
-
const ledgerRoot =
|
|
19049
|
-
const outputPath =
|
|
19301
|
+
const ledgerRoot = path40.join(options.memoryDir, "state", "observation-ledger");
|
|
19302
|
+
const outputPath = path40.join(ledgerRoot, "rebuilt-observations.jsonl");
|
|
19050
19303
|
const legacyFiles = await listLegacyObservationFiles(ledgerRoot);
|
|
19051
|
-
const sourceRelativePaths = legacyFiles.map((name) =>
|
|
19304
|
+
const sourceRelativePaths = legacyFiles.map((name) => path40.join("state", "observation-ledger", name));
|
|
19052
19305
|
const byKey = /* @__PURE__ */ new Map();
|
|
19053
19306
|
let parsedRows = 0;
|
|
19054
19307
|
let malformedLines = 0;
|
|
19055
19308
|
for (const file of legacyFiles) {
|
|
19056
|
-
const full =
|
|
19309
|
+
const full = path40.join(ledgerRoot, file);
|
|
19057
19310
|
const raw = await readFile28(full, "utf-8");
|
|
19058
19311
|
for (const line of raw.split("\n")) {
|
|
19059
19312
|
if (!line.trim()) continue;
|
|
@@ -19132,7 +19385,7 @@ async function migrateObservations(options) {
|
|
|
19132
19385
|
|
|
19133
19386
|
// src/network/tailscale.ts
|
|
19134
19387
|
import { stat as stat8 } from "fs/promises";
|
|
19135
|
-
import { spawn as
|
|
19388
|
+
import { spawn as spawn3 } from "child_process";
|
|
19136
19389
|
var TailscaleHelper = class {
|
|
19137
19390
|
tailscaleBinary;
|
|
19138
19391
|
rsyncBinary;
|
|
@@ -19203,7 +19456,7 @@ async function assertReadableDirectory(dir) {
|
|
|
19203
19456
|
}
|
|
19204
19457
|
var defaultCommandRunner = (command, args, options) => {
|
|
19205
19458
|
return new Promise((resolve) => {
|
|
19206
|
-
const child =
|
|
19459
|
+
const child = spawn3(command, args, {
|
|
19207
19460
|
stdio: ["ignore", "pipe", "pipe"]
|
|
19208
19461
|
});
|
|
19209
19462
|
let stdout = "";
|
|
@@ -19246,7 +19499,7 @@ import { createReadStream } from "fs";
|
|
|
19246
19499
|
import { mkdir as mkdir29, readdir as readdir17, realpath as realpath2, stat as stat9 } from "fs/promises";
|
|
19247
19500
|
import { createServer } from "http";
|
|
19248
19501
|
import { timingSafeEqual } from "crypto";
|
|
19249
|
-
import
|
|
19502
|
+
import path41 from "path";
|
|
19250
19503
|
import { pipeline } from "stream/promises";
|
|
19251
19504
|
import { URL as URL2 } from "url";
|
|
19252
19505
|
function hostToUrlAuthority(host) {
|
|
@@ -19282,10 +19535,10 @@ var WebDavServer = class _WebDavServer {
|
|
|
19282
19535
|
const allowedRoots = [];
|
|
19283
19536
|
const aliasSet = /* @__PURE__ */ new Set();
|
|
19284
19537
|
for (const dir of options.allowlistDirs) {
|
|
19285
|
-
const resolved =
|
|
19538
|
+
const resolved = path41.resolve(dir);
|
|
19286
19539
|
await mkdir29(resolved, { recursive: true });
|
|
19287
19540
|
const canonical = await realpath2(resolved);
|
|
19288
|
-
const alias =
|
|
19541
|
+
const alias = path41.basename(canonical) || "root";
|
|
19289
19542
|
if (aliasSet.has(alias)) {
|
|
19290
19543
|
throw new Error(`duplicate webdav allowlist alias: ${alias}`);
|
|
19291
19544
|
}
|
|
@@ -19441,7 +19694,7 @@ var WebDavServer = class _WebDavServer {
|
|
|
19441
19694
|
if (decodedPath.includes("\0")) {
|
|
19442
19695
|
return { ok: false, code: 400, message: "invalid path" };
|
|
19443
19696
|
}
|
|
19444
|
-
const normalized =
|
|
19697
|
+
const normalized = path41.posix.normalize(decodedPath);
|
|
19445
19698
|
const segments = normalized.split("/").filter((segment) => segment.length > 0);
|
|
19446
19699
|
if (segments.length === 0) {
|
|
19447
19700
|
return { ok: false, code: 403, message: "root listing is not allowed" };
|
|
@@ -19455,7 +19708,7 @@ var WebDavServer = class _WebDavServer {
|
|
|
19455
19708
|
if (relative.some((segment) => segment === ".." || segment.includes("\\"))) {
|
|
19456
19709
|
return { ok: false, code: 403, message: "path traversal is not allowed" };
|
|
19457
19710
|
}
|
|
19458
|
-
const candidate =
|
|
19711
|
+
const candidate = path41.resolve(root.absolute, ...relative);
|
|
19459
19712
|
if (!this.isPathInside(root.absolute, candidate)) {
|
|
19460
19713
|
return { ok: false, code: 403, message: "path escaped allowlist" };
|
|
19461
19714
|
}
|
|
@@ -19533,10 +19786,10 @@ var WebDavServer = class _WebDavServer {
|
|
|
19533
19786
|
}
|
|
19534
19787
|
isPathInside(root, target) {
|
|
19535
19788
|
if (target === root) return true;
|
|
19536
|
-
if (root ===
|
|
19789
|
+
if (root === path41.parse(root).root) {
|
|
19537
19790
|
return target.startsWith(root);
|
|
19538
19791
|
}
|
|
19539
|
-
return target.startsWith(`${root}${
|
|
19792
|
+
return target.startsWith(`${root}${path41.sep}`);
|
|
19540
19793
|
}
|
|
19541
19794
|
};
|
|
19542
19795
|
function xmlEscape(value) {
|
|
@@ -19548,8 +19801,8 @@ function toEncodedHref(pathname) {
|
|
|
19548
19801
|
|
|
19549
19802
|
// src/compat/checks.ts
|
|
19550
19803
|
import { access as access2, readFile as readFile29 } from "fs/promises";
|
|
19551
|
-
import
|
|
19552
|
-
import { spawn as
|
|
19804
|
+
import path42 from "path";
|
|
19805
|
+
import { spawn as spawn4 } from "child_process";
|
|
19553
19806
|
var REQUIRED_HOOKS = ["before_agent_start", "agent_end"];
|
|
19554
19807
|
function isSafeCommandToken(command) {
|
|
19555
19808
|
return /^[a-zA-Z0-9._-]+$/.test(command);
|
|
@@ -19560,7 +19813,7 @@ var defaultRunner = {
|
|
|
19560
19813
|
const binary = process.platform === "win32" ? "where" : "which";
|
|
19561
19814
|
const args = [command];
|
|
19562
19815
|
return new Promise((resolve) => {
|
|
19563
|
-
const child =
|
|
19816
|
+
const child = spawn4(binary, args, { stdio: "ignore" });
|
|
19564
19817
|
child.on("error", () => resolve(false));
|
|
19565
19818
|
child.on("close", (code) => resolve(code === 0));
|
|
19566
19819
|
});
|
|
@@ -19730,9 +19983,9 @@ function compareVersions(a, b) {
|
|
|
19730
19983
|
async function runCompatChecks(options) {
|
|
19731
19984
|
const checks = [];
|
|
19732
19985
|
const runner = options.runner ?? defaultRunner;
|
|
19733
|
-
const pluginJsonPath =
|
|
19734
|
-
const packageJsonPath =
|
|
19735
|
-
const indexPath =
|
|
19986
|
+
const pluginJsonPath = path42.join(options.repoRoot, "openclaw.plugin.json");
|
|
19987
|
+
const packageJsonPath = path42.join(options.repoRoot, "package.json");
|
|
19988
|
+
const indexPath = path42.join(options.repoRoot, "src", "index.ts");
|
|
19736
19989
|
let pluginRaw = "";
|
|
19737
19990
|
let pluginManifestPresent = false;
|
|
19738
19991
|
try {
|
|
@@ -20392,14 +20645,14 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
|
|
|
20392
20645
|
const ns = (namespace ?? "").trim();
|
|
20393
20646
|
if (!ns) return orchestrator.config.memoryDir;
|
|
20394
20647
|
if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
|
|
20395
|
-
const candidate =
|
|
20648
|
+
const candidate = path43.join(orchestrator.config.memoryDir, "namespaces", ns);
|
|
20396
20649
|
if (ns === orchestrator.config.defaultNamespace) {
|
|
20397
20650
|
return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
|
|
20398
20651
|
}
|
|
20399
20652
|
return candidate;
|
|
20400
20653
|
}
|
|
20401
20654
|
async function readAllMemoryFiles(memoryDir) {
|
|
20402
|
-
const roots = [
|
|
20655
|
+
const roots = [path43.join(memoryDir, "facts"), path43.join(memoryDir, "corrections")];
|
|
20403
20656
|
const out = [];
|
|
20404
20657
|
const walk = async (dir) => {
|
|
20405
20658
|
let entries;
|
|
@@ -20410,7 +20663,7 @@ async function readAllMemoryFiles(memoryDir) {
|
|
|
20410
20663
|
}
|
|
20411
20664
|
for (const entry of entries) {
|
|
20412
20665
|
const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
|
|
20413
|
-
const fullPath =
|
|
20666
|
+
const fullPath = path43.join(dir, entryName);
|
|
20414
20667
|
if (entry.isDirectory()) {
|
|
20415
20668
|
await walk(fullPath);
|
|
20416
20669
|
continue;
|
|
@@ -21182,7 +21435,7 @@ function registerCli(api, orchestrator) {
|
|
|
21182
21435
|
}
|
|
21183
21436
|
});
|
|
21184
21437
|
cmd.command("identity").description("Show agent identity reflections").action(async () => {
|
|
21185
|
-
const workspaceDir =
|
|
21438
|
+
const workspaceDir = path43.join(process.env.HOME ?? "~", ".openclaw", "workspace");
|
|
21186
21439
|
const identity = await orchestrator.storage.readIdentity(workspaceDir);
|
|
21187
21440
|
if (!identity) {
|
|
21188
21441
|
console.log("No identity file found.");
|
|
@@ -21405,8 +21658,8 @@ function registerCli(api, orchestrator) {
|
|
|
21405
21658
|
const options = args[0] ?? {};
|
|
21406
21659
|
const threadId = options.thread;
|
|
21407
21660
|
const top = parseInt(options.top ?? "10", 10);
|
|
21408
|
-
const memoryDir =
|
|
21409
|
-
const threading = new ThreadingManager(
|
|
21661
|
+
const memoryDir = path43.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
|
|
21662
|
+
const threading = new ThreadingManager(path43.join(memoryDir, "threads"));
|
|
21410
21663
|
if (threadId) {
|
|
21411
21664
|
const thread = await threading.loadThread(threadId);
|
|
21412
21665
|
if (!thread) {
|
|
@@ -21581,14 +21834,14 @@ function parseDuration(duration) {
|
|
|
21581
21834
|
// src/index.ts
|
|
21582
21835
|
import { readFile as readFile31, writeFile as writeFile26 } from "fs/promises";
|
|
21583
21836
|
import { readFileSync as readFileSync4 } from "fs";
|
|
21584
|
-
import
|
|
21837
|
+
import path44 from "path";
|
|
21585
21838
|
import os5 from "os";
|
|
21586
21839
|
var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
|
|
21587
21840
|
function loadPluginConfigFromFile() {
|
|
21588
21841
|
try {
|
|
21589
21842
|
const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
|
|
21590
21843
|
const homeDir = process.env.HOME ?? os5.homedir();
|
|
21591
|
-
const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath :
|
|
21844
|
+
const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path44.join(homeDir, ".openclaw", "openclaw.json");
|
|
21592
21845
|
const content = readFileSync4(configPath, "utf-8");
|
|
21593
21846
|
const config = JSON.parse(content);
|
|
21594
21847
|
const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
|
|
@@ -21796,7 +22049,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
|
|
|
21796
22049
|
);
|
|
21797
22050
|
async function ensureHourlySummaryCron(api2) {
|
|
21798
22051
|
const jobId = "engram-hourly-summary";
|
|
21799
|
-
const cronFilePath =
|
|
22052
|
+
const cronFilePath = path44.join(os5.homedir(), ".openclaw", "cron", "jobs.json");
|
|
21800
22053
|
try {
|
|
21801
22054
|
let jobsData = { version: 1, jobs: [] };
|
|
21802
22055
|
try {
|