@joshuaswarren/openclaw-engram 8.3.71 → 8.3.73
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 +193 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3650,7 +3650,11 @@ function getGlobalQmdState() {
|
|
|
3650
3650
|
lastGlobalUpdateFailAtMs: null,
|
|
3651
3651
|
lastGlobalEmbedRunAtMs: null,
|
|
3652
3652
|
lastGlobalEmbedFailAtMs: null,
|
|
3653
|
-
lastCliWarnAtMs: null
|
|
3653
|
+
lastCliWarnAtMs: null,
|
|
3654
|
+
lastUpdateByCollectionMs: {},
|
|
3655
|
+
lastUpdateFailByCollectionMs: {},
|
|
3656
|
+
lastEmbedByCollectionMs: {},
|
|
3657
|
+
lastEmbedFailByCollectionMs: {}
|
|
3654
3658
|
};
|
|
3655
3659
|
}
|
|
3656
3660
|
return g[QMD_GLOBAL_STATE_KEY];
|
|
@@ -4259,23 +4263,49 @@ ${stderr}`.trim();
|
|
|
4259
4263
|
}
|
|
4260
4264
|
}
|
|
4261
4265
|
async update() {
|
|
4266
|
+
await this.runUpdateForCollection(this.collection, { perCollectionThrottle: false });
|
|
4267
|
+
}
|
|
4268
|
+
async updateCollection(collection) {
|
|
4269
|
+
await this.runUpdateForCollection(collection, { perCollectionThrottle: true });
|
|
4270
|
+
}
|
|
4271
|
+
async runUpdateForCollection(collection, options) {
|
|
4262
4272
|
if (this.available === false) return;
|
|
4273
|
+
const name = collection.trim();
|
|
4274
|
+
if (!name) return;
|
|
4263
4275
|
const globalState = getGlobalQmdState();
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4276
|
+
const now = Date.now();
|
|
4277
|
+
if (options.perCollectionThrottle) {
|
|
4278
|
+
if (globalState.lastGlobalUpdateFailAtMs && now - globalState.lastGlobalUpdateFailAtMs < QMD_UPDATE_BACKOFF_MS) {
|
|
4279
|
+
log.debug("QMD update: suppressed by global failure backoff");
|
|
4280
|
+
return;
|
|
4281
|
+
}
|
|
4282
|
+
const lastCollectionRun = globalState.lastUpdateByCollectionMs[name];
|
|
4283
|
+
if (Number.isFinite(lastCollectionRun) && now - lastCollectionRun < this.updateMinIntervalMs) {
|
|
4284
|
+
log.debug(`QMD update: suppressed by per-collection min-interval gate (${name})`);
|
|
4285
|
+
return;
|
|
4286
|
+
}
|
|
4287
|
+
const lastCollectionFail = globalState.lastUpdateFailByCollectionMs[name];
|
|
4288
|
+
if (Number.isFinite(lastCollectionFail) && now - lastCollectionFail < QMD_UPDATE_BACKOFF_MS) {
|
|
4289
|
+
log.debug(`QMD update: suppressed by per-collection failure backoff (${name})`);
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
} else {
|
|
4293
|
+
if (this.lastUpdateRunAtMs && now - this.lastUpdateRunAtMs < this.updateMinIntervalMs) {
|
|
4294
|
+
log.debug("QMD update: suppressed due to min-interval gate");
|
|
4295
|
+
return;
|
|
4296
|
+
}
|
|
4297
|
+
if (this.lastUpdateFailAtMs && now - this.lastUpdateFailAtMs < QMD_UPDATE_BACKOFF_MS) {
|
|
4298
|
+
log.debug("QMD update: suppressed due to recent failures (backoff)");
|
|
4299
|
+
return;
|
|
4300
|
+
}
|
|
4301
|
+
if (globalState.lastGlobalUpdateRunAtMs && now - globalState.lastGlobalUpdateRunAtMs < this.updateMinIntervalMs) {
|
|
4302
|
+
log.debug("QMD update: suppressed by global min-interval gate");
|
|
4303
|
+
return;
|
|
4304
|
+
}
|
|
4305
|
+
if (globalState.lastGlobalUpdateFailAtMs && now - globalState.lastGlobalUpdateFailAtMs < QMD_UPDATE_BACKOFF_MS) {
|
|
4306
|
+
log.debug("QMD update: suppressed by global failure backoff");
|
|
4307
|
+
return;
|
|
4308
|
+
}
|
|
4279
4309
|
}
|
|
4280
4310
|
try {
|
|
4281
4311
|
if (!globalState.warnedGlobalUpdateBehavior) {
|
|
@@ -4285,21 +4315,31 @@ ${stderr}`.trim();
|
|
|
4285
4315
|
);
|
|
4286
4316
|
}
|
|
4287
4317
|
const startedAtMs = Date.now();
|
|
4288
|
-
await runQmd(["update", "-c",
|
|
4318
|
+
await runQmd(["update", "-c", name], this.updateTimeoutMs, this.qmdPath);
|
|
4289
4319
|
const durationMs = Date.now() - startedAtMs;
|
|
4290
4320
|
if (this.slowLog?.enabled && durationMs >= this.slowLog.thresholdMs) {
|
|
4291
4321
|
log.warn(`SLOW QMD update: durationMs=${durationMs}`);
|
|
4292
4322
|
}
|
|
4293
|
-
const
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4323
|
+
const at = Date.now();
|
|
4324
|
+
if (options.perCollectionThrottle) {
|
|
4325
|
+
globalState.lastUpdateByCollectionMs[name] = at;
|
|
4326
|
+
globalState.lastGlobalUpdateRunAtMs = at;
|
|
4327
|
+
} else {
|
|
4328
|
+
this.lastUpdateRunAtMs = at;
|
|
4329
|
+
globalState.lastGlobalUpdateRunAtMs = at;
|
|
4330
|
+
}
|
|
4331
|
+
log.debug(`QMD update completed for collection=${name}`);
|
|
4297
4332
|
} catch (err) {
|
|
4298
|
-
const
|
|
4299
|
-
|
|
4300
|
-
|
|
4333
|
+
const at = Date.now();
|
|
4334
|
+
if (options.perCollectionThrottle) {
|
|
4335
|
+
globalState.lastUpdateFailByCollectionMs[name] = at;
|
|
4336
|
+
globalState.lastGlobalUpdateFailAtMs = at;
|
|
4337
|
+
} else {
|
|
4338
|
+
this.lastUpdateFailAtMs = at;
|
|
4339
|
+
globalState.lastGlobalUpdateFailAtMs = at;
|
|
4340
|
+
}
|
|
4301
4341
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4302
|
-
log.warn(`QMD update failed: ${msg}`);
|
|
4342
|
+
log.warn(`QMD update failed for collection ${name}: ${msg}`);
|
|
4303
4343
|
}
|
|
4304
4344
|
}
|
|
4305
4345
|
async embed() {
|
|
@@ -4334,6 +4374,39 @@ ${stderr}`.trim();
|
|
|
4334
4374
|
log.warn(`QMD embed failed: ${msg}`);
|
|
4335
4375
|
}
|
|
4336
4376
|
}
|
|
4377
|
+
async embedCollection(collection) {
|
|
4378
|
+
if (this.available === false) return;
|
|
4379
|
+
const name = collection.trim();
|
|
4380
|
+
if (!name) return;
|
|
4381
|
+
const globalState = getGlobalQmdState();
|
|
4382
|
+
const now = Date.now();
|
|
4383
|
+
if (globalState.lastGlobalEmbedFailAtMs && now - globalState.lastGlobalEmbedFailAtMs < QMD_EMBED_BACKOFF_MS) {
|
|
4384
|
+
log.debug(`QMD embed: suppressed by global failure backoff (${name})`);
|
|
4385
|
+
return;
|
|
4386
|
+
}
|
|
4387
|
+
const lastCollectionRun = globalState.lastEmbedByCollectionMs[name];
|
|
4388
|
+
if (Number.isFinite(lastCollectionRun) && now - lastCollectionRun < this.updateMinIntervalMs) {
|
|
4389
|
+
log.debug(`QMD embed: suppressed by per-collection min-interval gate (${name})`);
|
|
4390
|
+
return;
|
|
4391
|
+
}
|
|
4392
|
+
const lastCollectionFail = globalState.lastEmbedFailByCollectionMs[name];
|
|
4393
|
+
if (Number.isFinite(lastCollectionFail) && now - lastCollectionFail < QMD_EMBED_BACKOFF_MS) {
|
|
4394
|
+
log.debug(`QMD embed: suppressed by per-collection failure backoff (${name})`);
|
|
4395
|
+
return;
|
|
4396
|
+
}
|
|
4397
|
+
try {
|
|
4398
|
+
await runQmd(["embed", "-c", name], 3e5, this.qmdPath);
|
|
4399
|
+
const at = Date.now();
|
|
4400
|
+
globalState.lastEmbedByCollectionMs[name] = at;
|
|
4401
|
+
globalState.lastGlobalEmbedRunAtMs = at;
|
|
4402
|
+
} catch (err) {
|
|
4403
|
+
const at = Date.now();
|
|
4404
|
+
globalState.lastEmbedFailByCollectionMs[name] = at;
|
|
4405
|
+
globalState.lastGlobalEmbedFailAtMs = at;
|
|
4406
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4407
|
+
log.warn(`QMD embed failed for collection ${name}: ${msg}`);
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4337
4410
|
async ensureCollection(memoryDir) {
|
|
4338
4411
|
if (this.available === false && !this.daemonAvailable) return "unknown";
|
|
4339
4412
|
if (this.available === false) return "skipped";
|
|
@@ -4364,7 +4437,7 @@ ${stderr}`.trim();
|
|
|
4364
4437
|
};
|
|
4365
4438
|
|
|
4366
4439
|
// src/storage.ts
|
|
4367
|
-
import { readdir, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink, appendFile } from "fs/promises";
|
|
4440
|
+
import { readdir, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink, rename, appendFile } from "fs/promises";
|
|
4368
4441
|
import { appendFileSync, mkdirSync as mkdirSync2, statSync } from "fs";
|
|
4369
4442
|
import { createHash } from "crypto";
|
|
4370
4443
|
import path4 from "path";
|
|
@@ -5579,6 +5652,86 @@ ${sanitized.text}
|
|
|
5579
5652
|
return null;
|
|
5580
5653
|
}
|
|
5581
5654
|
}
|
|
5655
|
+
resolveTierRootDir(tier) {
|
|
5656
|
+
return tier === "cold" ? path4.join(this.baseDir, "cold") : this.baseDir;
|
|
5657
|
+
}
|
|
5658
|
+
resolveMemoryDateDir(memory) {
|
|
5659
|
+
const preferred = memory.frontmatter.created || memory.frontmatter.updated;
|
|
5660
|
+
const dateToken = (preferred ?? "").slice(0, 10);
|
|
5661
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(dateToken) ? dateToken : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5662
|
+
}
|
|
5663
|
+
isArtifactMemory(memory) {
|
|
5664
|
+
if (memory.frontmatter.source === "artifact") return true;
|
|
5665
|
+
if (memory.frontmatter.artifactType !== void 0) return true;
|
|
5666
|
+
return /[\\/]artifacts[\\/]/.test(memory.path);
|
|
5667
|
+
}
|
|
5668
|
+
buildTierMemoryPath(memory, tier) {
|
|
5669
|
+
const root = this.resolveTierRootDir(tier);
|
|
5670
|
+
if (this.isArtifactMemory(memory)) {
|
|
5671
|
+
return path4.join(root, "artifacts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
5672
|
+
}
|
|
5673
|
+
if (memory.frontmatter.category === "correction") {
|
|
5674
|
+
return path4.join(root, "corrections", `${memory.frontmatter.id}.md`);
|
|
5675
|
+
}
|
|
5676
|
+
return path4.join(root, "facts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
5677
|
+
}
|
|
5678
|
+
async writeMemoryFileAtomic(targetPath, memory) {
|
|
5679
|
+
const fileContent = `${serializeFrontmatter(memory.frontmatter)}
|
|
5680
|
+
|
|
5681
|
+
${memory.content}
|
|
5682
|
+
`;
|
|
5683
|
+
await mkdir2(path4.dirname(targetPath), { recursive: true });
|
|
5684
|
+
const tempPath = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
|
|
5685
|
+
try {
|
|
5686
|
+
await writeFile2(tempPath, fileContent, "utf-8");
|
|
5687
|
+
await rename(tempPath, targetPath);
|
|
5688
|
+
} catch (err) {
|
|
5689
|
+
try {
|
|
5690
|
+
await unlink(tempPath);
|
|
5691
|
+
} catch {
|
|
5692
|
+
}
|
|
5693
|
+
throw err;
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
async moveMemoryToPath(memory, targetPath) {
|
|
5697
|
+
await this.writeMemoryFileAtomic(targetPath, memory);
|
|
5698
|
+
const sourcePath = path4.resolve(memory.path);
|
|
5699
|
+
const destPath = path4.resolve(targetPath);
|
|
5700
|
+
if (sourcePath !== destPath) {
|
|
5701
|
+
try {
|
|
5702
|
+
await unlink(memory.path);
|
|
5703
|
+
} catch (err) {
|
|
5704
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5705
|
+
if (!message.includes("ENOENT")) {
|
|
5706
|
+
throw err;
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
5711
|
+
async migrateMemoryToTier(memory, targetTier) {
|
|
5712
|
+
const targetPath = this.buildTierMemoryPath(memory, targetTier);
|
|
5713
|
+
const sourcePath = path4.resolve(memory.path);
|
|
5714
|
+
const destPath = path4.resolve(targetPath);
|
|
5715
|
+
if (sourcePath === destPath) {
|
|
5716
|
+
return { changed: false, targetPath };
|
|
5717
|
+
}
|
|
5718
|
+
const existing = await this.readMemoryByPath(targetPath);
|
|
5719
|
+
if (existing?.frontmatter.id === memory.frontmatter.id) {
|
|
5720
|
+
try {
|
|
5721
|
+
await unlink(memory.path);
|
|
5722
|
+
} catch (err) {
|
|
5723
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5724
|
+
if (!message.includes("ENOENT")) {
|
|
5725
|
+
throw err;
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
this.bumpMemoryStatusVersion();
|
|
5729
|
+
return { changed: false, targetPath };
|
|
5730
|
+
}
|
|
5731
|
+
await this.moveMemoryToPath(memory, targetPath);
|
|
5732
|
+
this.bumpMemoryStatusVersion();
|
|
5733
|
+
return { changed: true, targetPath };
|
|
5734
|
+
}
|
|
5582
5735
|
get archiveDir() {
|
|
5583
5736
|
return path4.join(this.baseDir, "archive");
|
|
5584
5737
|
}
|
|
@@ -10614,16 +10767,22 @@ function resolveLifecycleState(frontmatter) {
|
|
|
10614
10767
|
function computeHeat(memory, now, signals) {
|
|
10615
10768
|
const frontmatter = memory.frontmatter;
|
|
10616
10769
|
if (frontmatter.status === "archived") return 0;
|
|
10617
|
-
const
|
|
10618
|
-
const
|
|
10619
|
-
const access4 = accessWeight(frontmatter.accessCount);
|
|
10620
|
-
const recency = recencyWeight(frontmatter, nowMs);
|
|
10621
|
-
const importance = clamp01(frontmatter.importance?.score ?? 0.5);
|
|
10622
|
-
const feedback = feedbackWeight(signals);
|
|
10623
|
-
const disputedPenalty = frontmatter.verificationState === "disputed" ? 0.2 : 0;
|
|
10624
|
-
const score = confidence * 0.25 + access4 * 0.3 + recency * 0.2 + importance * 0.15 + feedback * 0.1 - disputedPenalty;
|
|
10770
|
+
const inputs = computeLifecycleValueInputs(memory, now, signals);
|
|
10771
|
+
const score = inputs.confidence * 0.25 + inputs.access * 0.3 + inputs.recency * 0.2 + inputs.importance * 0.15 + inputs.feedback * 0.1 - inputs.disputedPenalty;
|
|
10625
10772
|
return clamp01(score);
|
|
10626
10773
|
}
|
|
10774
|
+
function computeLifecycleValueInputs(memory, now, signals) {
|
|
10775
|
+
const frontmatter = memory.frontmatter;
|
|
10776
|
+
const nowMs = now.getTime();
|
|
10777
|
+
return {
|
|
10778
|
+
confidence: confidenceTierWeight(frontmatter),
|
|
10779
|
+
access: accessWeight(frontmatter.accessCount),
|
|
10780
|
+
recency: recencyWeight(frontmatter, nowMs),
|
|
10781
|
+
importance: clamp01(frontmatter.importance?.score ?? 0.5),
|
|
10782
|
+
feedback: feedbackWeight(signals),
|
|
10783
|
+
disputedPenalty: frontmatter.verificationState === "disputed" ? 0.2 : 0
|
|
10784
|
+
};
|
|
10785
|
+
}
|
|
10627
10786
|
function computeDecay(memory, now, signals) {
|
|
10628
10787
|
const frontmatter = memory.frontmatter;
|
|
10629
10788
|
if (frontmatter.status === "archived") return 1;
|