@joshuaswarren/openclaw-engram 9.0.29 → 9.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/index.js +713 -413
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +10 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -299,6 +299,7 @@ function parseConfig(raw) {
|
|
|
299
299
|
trustZonesEnabled: cfg.trustZonesEnabled === true,
|
|
300
300
|
quarantinePromotionEnabled: cfg.quarantinePromotionEnabled === true,
|
|
301
301
|
trustZoneStoreDir: typeof cfg.trustZoneStoreDir === "string" && cfg.trustZoneStoreDir.trim().length > 0 ? cfg.trustZoneStoreDir.trim() : path.join(memoryDir, "state", "trust-zones"),
|
|
302
|
+
trustZoneRecallEnabled: cfg.trustZoneRecallEnabled === true,
|
|
302
303
|
// Local LLM Provider (v2.1)
|
|
303
304
|
localLlmEnabled: cfg.localLlmEnabled === true || cfg.localLlmEnabled === "true",
|
|
304
305
|
// default: false
|
|
@@ -569,6 +570,12 @@ function buildDefaultRecallPipeline(cfg) {
|
|
|
569
570
|
maxResults: 3,
|
|
570
571
|
maxChars: 2200
|
|
571
572
|
},
|
|
573
|
+
{
|
|
574
|
+
id: "trust-zones",
|
|
575
|
+
enabled: cfg.trustZoneRecallEnabled === true,
|
|
576
|
+
maxResults: 3,
|
|
577
|
+
maxChars: 1800
|
|
578
|
+
},
|
|
572
579
|
{
|
|
573
580
|
id: "memories",
|
|
574
581
|
enabled: true,
|
|
@@ -616,10 +623,10 @@ function buildRecallPipelineConfig(cfg) {
|
|
|
616
623
|
}
|
|
617
624
|
|
|
618
625
|
// src/orchestrator.ts
|
|
619
|
-
import
|
|
626
|
+
import path34 from "path";
|
|
620
627
|
import os5 from "os";
|
|
621
628
|
import { createHash as createHash6 } from "crypto";
|
|
622
|
-
import { mkdir as
|
|
629
|
+
import { mkdir as mkdir24, readdir as readdir16, readFile as readFile23, stat as stat7, unlink as unlink5, writeFile as writeFile24 } from "fs/promises";
|
|
623
630
|
|
|
624
631
|
// src/signal.ts
|
|
625
632
|
var BUILTIN_HIGH_PATTERNS = [
|
|
@@ -14196,6 +14203,33 @@ function recencyWindowFromPrompt(prompt, nowMs = Date.now()) {
|
|
|
14196
14203
|
import path22 from "path";
|
|
14197
14204
|
import { mkdir as mkdir15, writeFile as writeFile16 } from "fs/promises";
|
|
14198
14205
|
|
|
14206
|
+
// src/recall-tokenization.ts
|
|
14207
|
+
function normalizeRecallTokens(value, extraStopWords = []) {
|
|
14208
|
+
const stopWords = /* @__PURE__ */ new Set([
|
|
14209
|
+
"the",
|
|
14210
|
+
"and",
|
|
14211
|
+
"for",
|
|
14212
|
+
"with",
|
|
14213
|
+
"from",
|
|
14214
|
+
"into",
|
|
14215
|
+
"that",
|
|
14216
|
+
"this",
|
|
14217
|
+
"why",
|
|
14218
|
+
"did",
|
|
14219
|
+
...extraStopWords
|
|
14220
|
+
]);
|
|
14221
|
+
return value.toLowerCase().split(/[^a-z0-9]+/).map((token) => token.trim()).filter((token) => token.length >= 3 && !stopWords.has(token));
|
|
14222
|
+
}
|
|
14223
|
+
function countRecallTokenOverlap(queryTokens, value, extraStopWords = []) {
|
|
14224
|
+
if (!value) return 0;
|
|
14225
|
+
const tokens = new Set(normalizeRecallTokens(value, extraStopWords));
|
|
14226
|
+
let matches = 0;
|
|
14227
|
+
for (const token of queryTokens) {
|
|
14228
|
+
if (tokens.has(token)) matches += 1;
|
|
14229
|
+
}
|
|
14230
|
+
return matches;
|
|
14231
|
+
}
|
|
14232
|
+
|
|
14199
14233
|
// src/store-contract.ts
|
|
14200
14234
|
function isRecord2(value) {
|
|
14201
14235
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -14323,19 +14357,6 @@ async function getCausalTrajectoryStoreStatus(options) {
|
|
|
14323
14357
|
invalidTrajectories
|
|
14324
14358
|
};
|
|
14325
14359
|
}
|
|
14326
|
-
function normalizeTokens(value) {
|
|
14327
|
-
const stopWords = /* @__PURE__ */ new Set(["the", "and", "for", "with", "from", "into", "that", "this", "why", "did", "make"]);
|
|
14328
|
-
return value.toLowerCase().split(/[^a-z0-9]+/).map((token) => token.trim()).filter((token) => token.length >= 3 && !stopWords.has(token));
|
|
14329
|
-
}
|
|
14330
|
-
function countOverlap(queryTokens, value) {
|
|
14331
|
-
if (!value) return 0;
|
|
14332
|
-
const tokens = new Set(normalizeTokens(value));
|
|
14333
|
-
let matches = 0;
|
|
14334
|
-
for (const token of queryTokens) {
|
|
14335
|
-
if (tokens.has(token)) matches += 1;
|
|
14336
|
-
}
|
|
14337
|
-
return matches;
|
|
14338
|
-
}
|
|
14339
14360
|
function lexicalScoreCausalTrajectoryRecord(record, queryTokens) {
|
|
14340
14361
|
const weightedFields = [
|
|
14341
14362
|
["goal", record.goal, 4],
|
|
@@ -14351,7 +14372,7 @@ function lexicalScoreCausalTrajectoryRecord(record, queryTokens) {
|
|
|
14351
14372
|
let score = 0;
|
|
14352
14373
|
const matchedFields = [];
|
|
14353
14374
|
for (const [field, value, weight] of weightedFields) {
|
|
14354
|
-
const matches =
|
|
14375
|
+
const matches = countRecallTokenOverlap(queryTokens, value, ["make"]);
|
|
14355
14376
|
if (matches > 0) matchedFields.push(field);
|
|
14356
14377
|
score += matches * weight;
|
|
14357
14378
|
}
|
|
@@ -14372,7 +14393,7 @@ async function searchCausalTrajectories(options) {
|
|
|
14372
14393
|
if (maxResults === 0) return [];
|
|
14373
14394
|
const { trajectories } = await readCausalTrajectoryRecords(options);
|
|
14374
14395
|
if (trajectories.length === 0) return [];
|
|
14375
|
-
const queryTokens = new Set(
|
|
14396
|
+
const queryTokens = new Set(normalizeRecallTokens(options.query, ["make"]));
|
|
14376
14397
|
if (queryTokens.size === 0) return [];
|
|
14377
14398
|
const scored = trajectories.map((record) => {
|
|
14378
14399
|
const lexical = lexicalScoreCausalTrajectoryRecord(record, queryTokens);
|
|
@@ -14515,12 +14536,12 @@ async function readObjectiveStateSnapshots(options) {
|
|
|
14515
14536
|
}
|
|
14516
14537
|
return { files, snapshots, invalidSnapshots };
|
|
14517
14538
|
}
|
|
14518
|
-
function
|
|
14539
|
+
function normalizeTokens(value) {
|
|
14519
14540
|
return value.toLowerCase().split(/[^a-z0-9]+/).map((token) => token.trim()).filter((token) => token.length >= 2);
|
|
14520
14541
|
}
|
|
14521
14542
|
function overlapScore(queryTokens, value, weight) {
|
|
14522
14543
|
if (!value) return 0;
|
|
14523
|
-
const tokens = new Set(
|
|
14544
|
+
const tokens = new Set(normalizeTokens(value));
|
|
14524
14545
|
let matches = 0;
|
|
14525
14546
|
for (const token of queryTokens) {
|
|
14526
14547
|
if (tokens.has(token)) matches += 1;
|
|
@@ -14555,7 +14576,7 @@ async function searchObjectiveStateSnapshots(options) {
|
|
|
14555
14576
|
if (maxResults === 0) return [];
|
|
14556
14577
|
const { snapshots } = await readObjectiveStateSnapshots(options);
|
|
14557
14578
|
if (snapshots.length === 0) return [];
|
|
14558
|
-
const queryTokens = new Set(
|
|
14579
|
+
const queryTokens = new Set(normalizeTokens(options.query));
|
|
14559
14580
|
const scored = snapshots.map((snapshot) => {
|
|
14560
14581
|
const lexicalScore = lexicalScoreObjectiveStateSnapshot(snapshot, queryTokens);
|
|
14561
14582
|
return {
|
|
@@ -14572,6 +14593,302 @@ async function searchObjectiveStateSnapshots(options) {
|
|
|
14572
14593
|
return filtered.slice(0, maxResults);
|
|
14573
14594
|
}
|
|
14574
14595
|
|
|
14596
|
+
// src/trust-zones.ts
|
|
14597
|
+
import path24 from "path";
|
|
14598
|
+
import { mkdir as mkdir17, writeFile as writeFile18 } from "fs/promises";
|
|
14599
|
+
function validateMetadata3(raw) {
|
|
14600
|
+
return validateStringRecord(raw, "metadata");
|
|
14601
|
+
}
|
|
14602
|
+
function validateZone(raw, field) {
|
|
14603
|
+
const value = assertString2(raw, field);
|
|
14604
|
+
if (!["quarantine", "working", "trusted"].includes(value)) {
|
|
14605
|
+
throw new Error(`${field} must be one of quarantine|working|trusted`);
|
|
14606
|
+
}
|
|
14607
|
+
return value;
|
|
14608
|
+
}
|
|
14609
|
+
function validateKind(raw) {
|
|
14610
|
+
const value = assertString2(raw, "kind");
|
|
14611
|
+
if (!["memory", "artifact", "state", "trajectory", "external"].includes(value)) {
|
|
14612
|
+
throw new Error("kind must be one of memory|artifact|state|trajectory|external");
|
|
14613
|
+
}
|
|
14614
|
+
return value;
|
|
14615
|
+
}
|
|
14616
|
+
function validateProvenance(raw) {
|
|
14617
|
+
if (!isRecord2(raw)) throw new Error("provenance must be an object");
|
|
14618
|
+
const sourceClass = assertString2(raw.sourceClass, "provenance.sourceClass");
|
|
14619
|
+
if (!["tool_output", "web_content", "subagent_trace", "system_memory", "user_input", "manual"].includes(sourceClass)) {
|
|
14620
|
+
throw new Error("provenance.sourceClass must be one of tool_output|web_content|subagent_trace|system_memory|user_input|manual");
|
|
14621
|
+
}
|
|
14622
|
+
return {
|
|
14623
|
+
sourceClass,
|
|
14624
|
+
observedAt: assertIsoRecordedAt(assertString2(raw.observedAt, "provenance.observedAt"), "provenance.observedAt"),
|
|
14625
|
+
sessionKey: optionalString(raw.sessionKey),
|
|
14626
|
+
sourceId: optionalString(raw.sourceId),
|
|
14627
|
+
evidenceHash: optionalString(raw.evidenceHash)
|
|
14628
|
+
};
|
|
14629
|
+
}
|
|
14630
|
+
function resolveTrustZoneStoreDir(memoryDir, overrideDir) {
|
|
14631
|
+
if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
|
|
14632
|
+
return overrideDir.trim();
|
|
14633
|
+
}
|
|
14634
|
+
return path24.join(memoryDir, "state", "trust-zones");
|
|
14635
|
+
}
|
|
14636
|
+
function validateTrustZoneRecord(raw) {
|
|
14637
|
+
if (!isRecord2(raw)) throw new Error("trust-zone record must be an object");
|
|
14638
|
+
if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
|
|
14639
|
+
return {
|
|
14640
|
+
schemaVersion: 1,
|
|
14641
|
+
recordId: assertSafePathSegment(assertString2(raw.recordId, "recordId"), "recordId"),
|
|
14642
|
+
zone: validateZone(raw.zone, "zone"),
|
|
14643
|
+
recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
|
|
14644
|
+
kind: validateKind(raw.kind),
|
|
14645
|
+
summary: assertString2(raw.summary, "summary"),
|
|
14646
|
+
provenance: validateProvenance(raw.provenance),
|
|
14647
|
+
promotedFromZone: raw.promotedFromZone === void 0 ? void 0 : validateZone(raw.promotedFromZone, "promotedFromZone"),
|
|
14648
|
+
entityRefs: optionalStringArray2(raw.entityRefs, "entityRefs"),
|
|
14649
|
+
tags: optionalStringArray2(raw.tags, "tags"),
|
|
14650
|
+
metadata: validateMetadata3(raw.metadata)
|
|
14651
|
+
};
|
|
14652
|
+
}
|
|
14653
|
+
async function recordTrustZoneRecord(options) {
|
|
14654
|
+
const rootDir = resolveTrustZoneStoreDir(options.memoryDir, options.trustZoneStoreDir);
|
|
14655
|
+
const validated = validateTrustZoneRecord(options.record);
|
|
14656
|
+
const day = recordStoreDay(validated.recordedAt);
|
|
14657
|
+
const zoneDir = path24.join(rootDir, "zones", validated.zone, day);
|
|
14658
|
+
const filePath = path24.join(zoneDir, `${validated.recordId}.json`);
|
|
14659
|
+
await mkdir17(zoneDir, { recursive: true });
|
|
14660
|
+
await writeFile18(filePath, JSON.stringify(validated, null, 2), "utf8");
|
|
14661
|
+
return filePath;
|
|
14662
|
+
}
|
|
14663
|
+
function hasAnchoredProvenance(record) {
|
|
14664
|
+
return Boolean(record.provenance.sourceId && record.provenance.evidenceHash);
|
|
14665
|
+
}
|
|
14666
|
+
function buildPromotionRecordId(sourceRecordId, targetZone, recordedAt) {
|
|
14667
|
+
const suffix = recordedAt.replace(/[^0-9]/g, "").slice(0, 14);
|
|
14668
|
+
return `${sourceRecordId}-${targetZone}-${suffix}`;
|
|
14669
|
+
}
|
|
14670
|
+
function dedupeStrings(values) {
|
|
14671
|
+
const out = values.filter((value) => typeof value === "string" && value.length > 0);
|
|
14672
|
+
if (out.length === 0) return void 0;
|
|
14673
|
+
return [...new Set(out)];
|
|
14674
|
+
}
|
|
14675
|
+
function planTrustZonePromotion(options) {
|
|
14676
|
+
const { record, targetZone } = options;
|
|
14677
|
+
const reasons = [];
|
|
14678
|
+
const provenanceAnchored = hasAnchoredProvenance(record);
|
|
14679
|
+
if (record.zone === targetZone) {
|
|
14680
|
+
reasons.push(`record is already in the ${targetZone} zone`);
|
|
14681
|
+
}
|
|
14682
|
+
if (record.zone === "trusted") {
|
|
14683
|
+
reasons.push("trusted records are terminal and cannot be promoted again");
|
|
14684
|
+
}
|
|
14685
|
+
if (record.zone === "quarantine" && targetZone === "trusted") {
|
|
14686
|
+
reasons.push("quarantine records must pass through working before trusted promotion");
|
|
14687
|
+
}
|
|
14688
|
+
if (record.zone === "working" && targetZone === "quarantine") {
|
|
14689
|
+
reasons.push("working records cannot be demoted back into quarantine in this promotion path");
|
|
14690
|
+
}
|
|
14691
|
+
if (record.zone === "quarantine" && targetZone !== "working") {
|
|
14692
|
+
reasons.push("quarantine promotions only support the working zone");
|
|
14693
|
+
}
|
|
14694
|
+
if (record.zone === "working" && targetZone !== "trusted") {
|
|
14695
|
+
reasons.push("working promotions only support the trusted zone");
|
|
14696
|
+
}
|
|
14697
|
+
if (targetZone === "trusted" && ["tool_output", "web_content", "subagent_trace"].includes(record.provenance.sourceClass) && provenanceAnchored !== true) {
|
|
14698
|
+
reasons.push("trusted promotion for external/tool-derived provenance requires both provenance.sourceId and provenance.evidenceHash");
|
|
14699
|
+
}
|
|
14700
|
+
return {
|
|
14701
|
+
allowed: reasons.length === 0,
|
|
14702
|
+
reasons,
|
|
14703
|
+
sourceRecordId: record.recordId,
|
|
14704
|
+
sourceZone: record.zone,
|
|
14705
|
+
targetZone,
|
|
14706
|
+
provenanceAnchored
|
|
14707
|
+
};
|
|
14708
|
+
}
|
|
14709
|
+
async function findTrustZoneRecordById(options) {
|
|
14710
|
+
const { records } = await readTrustZoneRecords(options);
|
|
14711
|
+
records.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
|
|
14712
|
+
return records.find((record) => record.recordId === options.recordId) ?? null;
|
|
14713
|
+
}
|
|
14714
|
+
async function promoteTrustZoneRecord(options) {
|
|
14715
|
+
if (options.enabled !== true) {
|
|
14716
|
+
throw new Error("trust zone promotion requires trustZonesEnabled=true");
|
|
14717
|
+
}
|
|
14718
|
+
if (options.promotionEnabled !== true) {
|
|
14719
|
+
throw new Error("trust zone promotion requires quarantinePromotionEnabled=true");
|
|
14720
|
+
}
|
|
14721
|
+
const sourceRecord = await findTrustZoneRecordById({
|
|
14722
|
+
memoryDir: options.memoryDir,
|
|
14723
|
+
trustZoneStoreDir: options.trustZoneStoreDir,
|
|
14724
|
+
recordId: assertSafePathSegment(assertString2(options.sourceRecordId, "sourceRecordId"), "sourceRecordId")
|
|
14725
|
+
});
|
|
14726
|
+
if (!sourceRecord) {
|
|
14727
|
+
throw new Error(`source trust-zone record not found: ${options.sourceRecordId}`);
|
|
14728
|
+
}
|
|
14729
|
+
const plan = planTrustZonePromotion({
|
|
14730
|
+
record: sourceRecord,
|
|
14731
|
+
targetZone: options.targetZone
|
|
14732
|
+
});
|
|
14733
|
+
if (!plan.allowed) {
|
|
14734
|
+
throw new Error(`trust-zone promotion denied: ${plan.reasons.join("; ")}`);
|
|
14735
|
+
}
|
|
14736
|
+
const recordedAt = assertIsoRecordedAt(assertString2(options.recordedAt, "recordedAt"));
|
|
14737
|
+
const promotionReason = assertString2(options.promotionReason, "promotionReason");
|
|
14738
|
+
const nextRecord = {
|
|
14739
|
+
schemaVersion: 1,
|
|
14740
|
+
recordId: buildPromotionRecordId(sourceRecord.recordId, options.targetZone, recordedAt),
|
|
14741
|
+
zone: options.targetZone,
|
|
14742
|
+
recordedAt,
|
|
14743
|
+
kind: sourceRecord.kind,
|
|
14744
|
+
summary: optionalString(options.summary) ?? sourceRecord.summary,
|
|
14745
|
+
provenance: sourceRecord.provenance,
|
|
14746
|
+
promotedFromZone: sourceRecord.zone,
|
|
14747
|
+
entityRefs: sourceRecord.entityRefs,
|
|
14748
|
+
tags: dedupeStrings([...sourceRecord.tags ?? [], "promotion"]),
|
|
14749
|
+
metadata: {
|
|
14750
|
+
...sourceRecord.metadata ?? {},
|
|
14751
|
+
sourceRecordId: sourceRecord.recordId,
|
|
14752
|
+
promotionReason
|
|
14753
|
+
}
|
|
14754
|
+
};
|
|
14755
|
+
if (options.dryRun === true) {
|
|
14756
|
+
return {
|
|
14757
|
+
plan,
|
|
14758
|
+
wroteRecord: false,
|
|
14759
|
+
record: nextRecord,
|
|
14760
|
+
sourceRecord
|
|
14761
|
+
};
|
|
14762
|
+
}
|
|
14763
|
+
const filePath = await recordTrustZoneRecord({
|
|
14764
|
+
memoryDir: options.memoryDir,
|
|
14765
|
+
trustZoneStoreDir: options.trustZoneStoreDir,
|
|
14766
|
+
record: nextRecord
|
|
14767
|
+
});
|
|
14768
|
+
return {
|
|
14769
|
+
plan,
|
|
14770
|
+
wroteRecord: true,
|
|
14771
|
+
record: nextRecord,
|
|
14772
|
+
filePath,
|
|
14773
|
+
sourceRecord
|
|
14774
|
+
};
|
|
14775
|
+
}
|
|
14776
|
+
async function readTrustZoneRecords(options) {
|
|
14777
|
+
const rootDir = resolveTrustZoneStoreDir(options.memoryDir, options.trustZoneStoreDir);
|
|
14778
|
+
const files = await listJsonFiles(path24.join(rootDir, "zones"));
|
|
14779
|
+
const records = [];
|
|
14780
|
+
const invalidRecords = [];
|
|
14781
|
+
for (const filePath of files) {
|
|
14782
|
+
try {
|
|
14783
|
+
records.push(validateTrustZoneRecord(await readJsonFile(filePath)));
|
|
14784
|
+
} catch (error) {
|
|
14785
|
+
invalidRecords.push({
|
|
14786
|
+
path: filePath,
|
|
14787
|
+
error: error instanceof Error ? error.message : String(error)
|
|
14788
|
+
});
|
|
14789
|
+
}
|
|
14790
|
+
}
|
|
14791
|
+
return { files, records, invalidRecords };
|
|
14792
|
+
}
|
|
14793
|
+
function lexicalScoreTrustZoneRecord(record, queryTokens) {
|
|
14794
|
+
const weightedFields = [
|
|
14795
|
+
["summary", record.summary, 4],
|
|
14796
|
+
["kind", record.kind, 1],
|
|
14797
|
+
["zone", record.zone, 1],
|
|
14798
|
+
["sourceClass", record.provenance.sourceClass, 1],
|
|
14799
|
+
["entityRefs", record.entityRefs?.join(" "), 2],
|
|
14800
|
+
["tags", record.tags?.join(" "), 2],
|
|
14801
|
+
["metadata", record.metadata ? Object.values(record.metadata).join(" ") : void 0, 1]
|
|
14802
|
+
];
|
|
14803
|
+
let score = 0;
|
|
14804
|
+
const matchedFields = [];
|
|
14805
|
+
for (const [field, value, weight] of weightedFields) {
|
|
14806
|
+
const matches = countRecallTokenOverlap(queryTokens, value, ["what"]);
|
|
14807
|
+
if (matches > 0) matchedFields.push(field);
|
|
14808
|
+
score += matches * weight;
|
|
14809
|
+
}
|
|
14810
|
+
return { score, matchedFields };
|
|
14811
|
+
}
|
|
14812
|
+
function zonePriority(zone) {
|
|
14813
|
+
switch (zone) {
|
|
14814
|
+
case "trusted":
|
|
14815
|
+
return 3;
|
|
14816
|
+
case "working":
|
|
14817
|
+
return 2;
|
|
14818
|
+
case "quarantine":
|
|
14819
|
+
return 1;
|
|
14820
|
+
}
|
|
14821
|
+
}
|
|
14822
|
+
function scoreTrustZoneRecord(record, lexicalScore, sessionKey) {
|
|
14823
|
+
let score = lexicalScore;
|
|
14824
|
+
score += zonePriority(record.zone);
|
|
14825
|
+
if (sessionKey && record.provenance.sessionKey === sessionKey) score += 1;
|
|
14826
|
+
const recordedAtMs = Date.parse(record.recordedAt);
|
|
14827
|
+
if (Number.isFinite(recordedAtMs)) {
|
|
14828
|
+
const ageHours = Math.max(0, (Date.now() - recordedAtMs) / 36e5);
|
|
14829
|
+
score += 1 / (1 + ageHours);
|
|
14830
|
+
}
|
|
14831
|
+
return score;
|
|
14832
|
+
}
|
|
14833
|
+
async function searchTrustZoneRecords(options) {
|
|
14834
|
+
const maxResults = Math.max(0, Math.floor(options.maxResults));
|
|
14835
|
+
if (maxResults === 0) return [];
|
|
14836
|
+
const { records } = await readTrustZoneRecords(options);
|
|
14837
|
+
const candidates = records.filter((record) => record.zone !== "quarantine");
|
|
14838
|
+
if (candidates.length === 0) return [];
|
|
14839
|
+
const queryTokens = new Set(normalizeRecallTokens(options.query, ["what"]));
|
|
14840
|
+
if (queryTokens.size === 0) return [];
|
|
14841
|
+
const scored = candidates.map((record) => {
|
|
14842
|
+
const lexical = lexicalScoreTrustZoneRecord(record, queryTokens);
|
|
14843
|
+
return {
|
|
14844
|
+
record,
|
|
14845
|
+
matchedFields: lexical.matchedFields,
|
|
14846
|
+
lexicalScore: lexical.score,
|
|
14847
|
+
score: scoreTrustZoneRecord(record, lexical.score, options.sessionKey)
|
|
14848
|
+
};
|
|
14849
|
+
});
|
|
14850
|
+
const filtered = scored.filter((result) => result.lexicalScore > 0);
|
|
14851
|
+
filtered.sort((left, right) => {
|
|
14852
|
+
if (right.score !== left.score) return right.score - left.score;
|
|
14853
|
+
return right.record.recordedAt.localeCompare(left.record.recordedAt);
|
|
14854
|
+
});
|
|
14855
|
+
return filtered.slice(0, maxResults).map(({ record, score, matchedFields }) => ({
|
|
14856
|
+
record,
|
|
14857
|
+
score,
|
|
14858
|
+
matchedFields
|
|
14859
|
+
}));
|
|
14860
|
+
}
|
|
14861
|
+
async function getTrustZoneStoreStatus(options) {
|
|
14862
|
+
const rootDir = resolveTrustZoneStoreDir(options.memoryDir, options.trustZoneStoreDir);
|
|
14863
|
+
const zonesDir = path24.join(rootDir, "zones");
|
|
14864
|
+
const { files, records, invalidRecords } = await readTrustZoneRecords(options);
|
|
14865
|
+
records.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
|
|
14866
|
+
const byZone = {};
|
|
14867
|
+
const byKind = {};
|
|
14868
|
+
for (const record of records) {
|
|
14869
|
+
byZone[record.zone] = (byZone[record.zone] ?? 0) + 1;
|
|
14870
|
+
byKind[record.kind] = (byKind[record.kind] ?? 0) + 1;
|
|
14871
|
+
}
|
|
14872
|
+
return {
|
|
14873
|
+
enabled: options.enabled,
|
|
14874
|
+
promotionEnabled: options.promotionEnabled,
|
|
14875
|
+
rootDir,
|
|
14876
|
+
zonesDir,
|
|
14877
|
+
records: {
|
|
14878
|
+
total: files.length,
|
|
14879
|
+
valid: records.length,
|
|
14880
|
+
invalid: invalidRecords.length,
|
|
14881
|
+
byZone,
|
|
14882
|
+
byKind,
|
|
14883
|
+
latestRecordId: records[0]?.recordId,
|
|
14884
|
+
latestRecordedAt: records[0]?.recordedAt,
|
|
14885
|
+
latestZone: records[0]?.zone
|
|
14886
|
+
},
|
|
14887
|
+
latestRecord: records[0],
|
|
14888
|
+
invalidRecords
|
|
14889
|
+
};
|
|
14890
|
+
}
|
|
14891
|
+
|
|
14575
14892
|
// src/replay/types.ts
|
|
14576
14893
|
var VALID_SOURCES = /* @__PURE__ */ new Set(["openclaw", "claude", "chatgpt"]);
|
|
14577
14894
|
var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
@@ -14680,8 +14997,8 @@ function chunkTranscriptEntries(sessionKey, entries, opts) {
|
|
|
14680
14997
|
}
|
|
14681
14998
|
|
|
14682
14999
|
// src/conversation-index/indexer.ts
|
|
14683
|
-
import { mkdir as
|
|
14684
|
-
import
|
|
15000
|
+
import { mkdir as mkdir18, writeFile as writeFile19 } from "fs/promises";
|
|
15001
|
+
import path25 from "path";
|
|
14685
15002
|
function sanitizeSessionKey(sessionKey) {
|
|
14686
15003
|
const raw = typeof sessionKey === "string" && sessionKey.trim().length > 0 ? sessionKey : "unknown-session";
|
|
14687
15004
|
return raw.toLowerCase().replace(/[^a-z0-9._-]+/g, "_").slice(0, 200);
|
|
@@ -14691,9 +15008,9 @@ async function writeConversationChunks(rootDir, chunks) {
|
|
|
14691
15008
|
for (const c of chunks) {
|
|
14692
15009
|
const safe = sanitizeSessionKey(c.sessionKey);
|
|
14693
15010
|
const date = c.startTs.slice(0, 10);
|
|
14694
|
-
const dir =
|
|
14695
|
-
await
|
|
14696
|
-
const fp =
|
|
15011
|
+
const dir = path25.join(rootDir, safe, date);
|
|
15012
|
+
await mkdir18(dir, { recursive: true });
|
|
15013
|
+
const fp = path25.join(dir, `${c.id}.md`);
|
|
14697
15014
|
const content = `---
|
|
14698
15015
|
kind: conversation_chunk
|
|
14699
15016
|
sessionKey: ${c.sessionKey}
|
|
@@ -14702,7 +15019,7 @@ endTs: ${c.endTs}
|
|
|
14702
15019
|
---
|
|
14703
15020
|
|
|
14704
15021
|
` + c.text + "\n";
|
|
14705
|
-
await
|
|
15022
|
+
await writeFile19(fp, content, "utf-8");
|
|
14706
15023
|
written.push(fp);
|
|
14707
15024
|
}
|
|
14708
15025
|
return written;
|
|
@@ -14722,7 +15039,7 @@ async function upsertConversationChunksFailOpen(adapter, chunks) {
|
|
|
14722
15039
|
|
|
14723
15040
|
// src/conversation-index/cleanup.ts
|
|
14724
15041
|
import { readdir as readdir13, rm as rm2 } from "fs/promises";
|
|
14725
|
-
import
|
|
15042
|
+
import path26 from "path";
|
|
14726
15043
|
async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
14727
15044
|
if (!Number.isFinite(retentionDays) || retentionDays <= 0) return;
|
|
14728
15045
|
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
@@ -14730,7 +15047,7 @@ async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
|
14730
15047
|
const sessions = await readdir13(rootDir, { withFileTypes: true });
|
|
14731
15048
|
for (const s of sessions) {
|
|
14732
15049
|
if (!s.isDirectory()) continue;
|
|
14733
|
-
const sessionDir =
|
|
15050
|
+
const sessionDir = path26.join(rootDir, s.name);
|
|
14734
15051
|
const dayDirs = await readdir13(sessionDir, { withFileTypes: true });
|
|
14735
15052
|
for (const d of dayDirs) {
|
|
14736
15053
|
if (!d.isDirectory()) continue;
|
|
@@ -14738,7 +15055,7 @@ async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
|
14738
15055
|
const dayMs = (/* @__PURE__ */ new Date(d.name + "T00:00:00.000Z")).getTime();
|
|
14739
15056
|
if (!Number.isFinite(dayMs)) continue;
|
|
14740
15057
|
if (dayMs < cutoffMs) {
|
|
14741
|
-
await rm2(
|
|
15058
|
+
await rm2(path26.join(sessionDir, d.name), { recursive: true, force: true });
|
|
14742
15059
|
}
|
|
14743
15060
|
}
|
|
14744
15061
|
try {
|
|
@@ -14757,7 +15074,7 @@ async function cleanupConversationChunks(rootDir, retentionDays) {
|
|
|
14757
15074
|
// src/conversation-index/faiss-adapter.ts
|
|
14758
15075
|
import * as childProcess from "child_process";
|
|
14759
15076
|
import { fileURLToPath } from "url";
|
|
14760
|
-
import
|
|
15077
|
+
import path27 from "path";
|
|
14761
15078
|
var FaissAdapterError = class extends Error {
|
|
14762
15079
|
constructor(message, code) {
|
|
14763
15080
|
super(message);
|
|
@@ -14767,18 +15084,18 @@ var FaissAdapterError = class extends Error {
|
|
|
14767
15084
|
};
|
|
14768
15085
|
function resolveDefaultFaissScriptPath(fromModuleUrl = import.meta.url) {
|
|
14769
15086
|
const currentFile = fileURLToPath(fromModuleUrl);
|
|
14770
|
-
const moduleDir =
|
|
14771
|
-
if (moduleDir.endsWith(`${
|
|
14772
|
-
return
|
|
15087
|
+
const moduleDir = path27.dirname(currentFile);
|
|
15088
|
+
if (moduleDir.endsWith(`${path27.sep}conversation-index`)) {
|
|
15089
|
+
return path27.resolve(moduleDir, "..", "..", "scripts", "faiss_index.py");
|
|
14773
15090
|
}
|
|
14774
|
-
return
|
|
15091
|
+
return path27.resolve(moduleDir, "..", "scripts", "faiss_index.py");
|
|
14775
15092
|
}
|
|
14776
15093
|
var FaissConversationIndexAdapter = class {
|
|
14777
15094
|
constructor(config) {
|
|
14778
15095
|
this.config = config;
|
|
14779
15096
|
this.pythonBin = config.pythonBin && config.pythonBin.trim().length > 0 ? config.pythonBin.trim() : "python3";
|
|
14780
15097
|
this.scriptPath = config.scriptPath && config.scriptPath.trim().length > 0 ? config.scriptPath.trim() : resolveDefaultFaissScriptPath();
|
|
14781
|
-
this.indexPath =
|
|
15098
|
+
this.indexPath = path27.isAbsolute(config.indexDir) ? config.indexDir : path27.join(config.memoryDir, config.indexDir);
|
|
14782
15099
|
this.spawnFn = config.spawnFn ?? childProcess.spawn;
|
|
14783
15100
|
}
|
|
14784
15101
|
pythonBin;
|
|
@@ -14959,7 +15276,7 @@ async function searchConversationIndexFaissFailOpen(adapter, query, maxResults)
|
|
|
14959
15276
|
}
|
|
14960
15277
|
|
|
14961
15278
|
// src/namespaces/storage.ts
|
|
14962
|
-
import
|
|
15279
|
+
import path28 from "path";
|
|
14963
15280
|
import { access } from "fs/promises";
|
|
14964
15281
|
async function exists(p) {
|
|
14965
15282
|
try {
|
|
@@ -14981,7 +15298,7 @@ var NamespaceStorageRouter = class {
|
|
|
14981
15298
|
this.defaultNsRootResolved = this.config.memoryDir;
|
|
14982
15299
|
return this.defaultNsRootResolved;
|
|
14983
15300
|
}
|
|
14984
|
-
const nsDir =
|
|
15301
|
+
const nsDir = path28.join(this.config.memoryDir, "namespaces", this.config.defaultNamespace);
|
|
14985
15302
|
this.defaultNsRootResolved = await exists(nsDir) ? nsDir : this.config.memoryDir;
|
|
14986
15303
|
return this.defaultNsRootResolved;
|
|
14987
15304
|
}
|
|
@@ -14990,7 +15307,7 @@ var NamespaceStorageRouter = class {
|
|
|
14990
15307
|
if (namespace === this.config.defaultNamespace) {
|
|
14991
15308
|
return this.defaultNsRootResolved ?? this.config.memoryDir;
|
|
14992
15309
|
}
|
|
14993
|
-
return
|
|
15310
|
+
return path28.join(this.config.memoryDir, "namespaces", namespace);
|
|
14994
15311
|
}
|
|
14995
15312
|
async storageFor(namespace) {
|
|
14996
15313
|
const ns = namespace || this.config.defaultNamespace;
|
|
@@ -15066,8 +15383,8 @@ function recallNamespacesForPrincipal(principal, config) {
|
|
|
15066
15383
|
}
|
|
15067
15384
|
|
|
15068
15385
|
// src/shared-context/manager.ts
|
|
15069
|
-
import { mkdir as
|
|
15070
|
-
import
|
|
15386
|
+
import { mkdir as mkdir19, readFile as readFile19, readdir as readdir14, appendFile as appendFile4, writeFile as writeFile20, stat as stat5 } from "fs/promises";
|
|
15387
|
+
import path29 from "path";
|
|
15071
15388
|
import os3 from "os";
|
|
15072
15389
|
import { z as z3 } from "zod";
|
|
15073
15390
|
var SharedFeedbackEntrySchema = z3.object({
|
|
@@ -15263,15 +15580,15 @@ async function computeSemanticOverlapsWithTimeout(sources, timeoutMs, maxCandida
|
|
|
15263
15580
|
var SharedContextManager = class {
|
|
15264
15581
|
constructor(config) {
|
|
15265
15582
|
this.config = config;
|
|
15266
|
-
const base = typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0 ? config.sharedContextDir :
|
|
15583
|
+
const base = typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0 ? config.sharedContextDir : path29.join(os3.homedir(), ".openclaw", "workspace", "shared-context");
|
|
15267
15584
|
this.dir = base;
|
|
15268
|
-
this.prioritiesPath =
|
|
15269
|
-
this.prioritiesInboxPath =
|
|
15270
|
-
this.outputsDir =
|
|
15271
|
-
this.roundtableDir =
|
|
15272
|
-
this.feedbackDir =
|
|
15273
|
-
this.feedbackInboxPath =
|
|
15274
|
-
this.crossSignalsDir =
|
|
15585
|
+
this.prioritiesPath = path29.join(base, "priorities.md");
|
|
15586
|
+
this.prioritiesInboxPath = path29.join(base, "priorities.inbox.md");
|
|
15587
|
+
this.outputsDir = path29.join(base, "agent-outputs");
|
|
15588
|
+
this.roundtableDir = path29.join(base, "roundtable");
|
|
15589
|
+
this.feedbackDir = path29.join(base, "feedback");
|
|
15590
|
+
this.feedbackInboxPath = path29.join(this.feedbackDir, "inbox.jsonl");
|
|
15591
|
+
this.crossSignalsDir = path29.join(base, "cross-signals");
|
|
15275
15592
|
}
|
|
15276
15593
|
dir;
|
|
15277
15594
|
prioritiesPath;
|
|
@@ -15282,15 +15599,15 @@ var SharedContextManager = class {
|
|
|
15282
15599
|
feedbackInboxPath;
|
|
15283
15600
|
crossSignalsDir;
|
|
15284
15601
|
async ensureStructure() {
|
|
15285
|
-
await
|
|
15286
|
-
await
|
|
15287
|
-
await
|
|
15288
|
-
await
|
|
15289
|
-
await
|
|
15290
|
-
await
|
|
15291
|
-
await
|
|
15292
|
-
await
|
|
15293
|
-
await
|
|
15602
|
+
await mkdir19(this.dir, { recursive: true });
|
|
15603
|
+
await mkdir19(this.outputsDir, { recursive: true });
|
|
15604
|
+
await mkdir19(this.roundtableDir, { recursive: true });
|
|
15605
|
+
await mkdir19(this.feedbackDir, { recursive: true });
|
|
15606
|
+
await mkdir19(this.crossSignalsDir, { recursive: true });
|
|
15607
|
+
await mkdir19(path29.join(this.dir, "staging"), { recursive: true });
|
|
15608
|
+
await mkdir19(path29.join(this.dir, "kpis"), { recursive: true });
|
|
15609
|
+
await mkdir19(path29.join(this.dir, "calendar"), { recursive: true });
|
|
15610
|
+
await mkdir19(path29.join(this.dir, "content-calendar"), { recursive: true });
|
|
15294
15611
|
await this.ensureFile(
|
|
15295
15612
|
this.prioritiesPath,
|
|
15296
15613
|
[
|
|
@@ -15321,7 +15638,7 @@ var SharedContextManager = class {
|
|
|
15321
15638
|
try {
|
|
15322
15639
|
await stat5(fp);
|
|
15323
15640
|
} catch {
|
|
15324
|
-
await
|
|
15641
|
+
await writeFile20(fp, content, "utf-8");
|
|
15325
15642
|
}
|
|
15326
15643
|
}
|
|
15327
15644
|
async readPriorities() {
|
|
@@ -15334,7 +15651,7 @@ var SharedContextManager = class {
|
|
|
15334
15651
|
async readLatestRoundtable() {
|
|
15335
15652
|
try {
|
|
15336
15653
|
const files = (await readdir14(this.roundtableDir)).filter((f) => f.endsWith(".md")).sort().reverse();
|
|
15337
|
-
const fp = files[0] ?
|
|
15654
|
+
const fp = files[0] ? path29.join(this.roundtableDir, files[0]) : null;
|
|
15338
15655
|
if (!fp) return "";
|
|
15339
15656
|
return await readFile19(fp, "utf-8");
|
|
15340
15657
|
} catch {
|
|
@@ -15346,9 +15663,9 @@ var SharedContextManager = class {
|
|
|
15346
15663
|
const date = ymd(createdAt);
|
|
15347
15664
|
const time = createdAt.toISOString().slice(11, 19).replace(/:/g, "");
|
|
15348
15665
|
const slug = safeSlug(opts.title);
|
|
15349
|
-
const dir =
|
|
15350
|
-
await
|
|
15351
|
-
const fp =
|
|
15666
|
+
const dir = path29.join(this.outputsDir, opts.agentId, date);
|
|
15667
|
+
await mkdir19(dir, { recursive: true });
|
|
15668
|
+
const fp = path29.join(dir, `${time}-${slug}.md`);
|
|
15352
15669
|
const body = `---
|
|
15353
15670
|
kind: agent_output
|
|
15354
15671
|
agent: ${opts.agentId}
|
|
@@ -15357,7 +15674,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
|
|
|
15357
15674
|
---
|
|
15358
15675
|
|
|
15359
15676
|
` + opts.content.trimEnd() + "\n";
|
|
15360
|
-
await
|
|
15677
|
+
await writeFile20(fp, body, "utf-8");
|
|
15361
15678
|
return fp;
|
|
15362
15679
|
}
|
|
15363
15680
|
async appendFeedback(entry) {
|
|
@@ -15383,11 +15700,11 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
|
|
|
15383
15700
|
const agents = await readdir14(this.outputsDir, { withFileTypes: true });
|
|
15384
15701
|
for (const a of agents) {
|
|
15385
15702
|
if (!a.isDirectory()) continue;
|
|
15386
|
-
const dayDir =
|
|
15703
|
+
const dayDir = path29.join(this.outputsDir, a.name, date);
|
|
15387
15704
|
try {
|
|
15388
15705
|
const files = (await readdir14(dayDir)).filter((f) => f.endsWith(".md")).sort();
|
|
15389
15706
|
for (const f of files) {
|
|
15390
|
-
const p =
|
|
15707
|
+
const p = path29.join(dayDir, f);
|
|
15391
15708
|
const raw = await readFile19(p, "utf-8");
|
|
15392
15709
|
const title = (raw.match(/^title:\s*(.+)$/m)?.[1] ?? f).trim();
|
|
15393
15710
|
outputs.push({ agent: a.name, path: p, title, raw });
|
|
@@ -15494,8 +15811,8 @@ ${body}`)
|
|
|
15494
15811
|
addedOverlapCount: semanticAddedOverlapCount
|
|
15495
15812
|
}
|
|
15496
15813
|
};
|
|
15497
|
-
const crossSignalsPath =
|
|
15498
|
-
await
|
|
15814
|
+
const crossSignalsPath = path29.join(this.crossSignalsDir, `${date}.json`);
|
|
15815
|
+
await writeFile20(crossSignalsPath, `${JSON.stringify(crossSignalReport, null, 2)}
|
|
15499
15816
|
`, "utf-8");
|
|
15500
15817
|
const overlapBullets = mergedOverlaps.length === 0 ? ["- No multi-agent topic overlap detected."] : mergedOverlaps.slice(0, 8).map((entry) => `- \`${entry.token}\` (${entry.agentCount} agents: ${entry.agents.join(", ")})`);
|
|
15501
15818
|
const md = [
|
|
@@ -15518,8 +15835,8 @@ ${body}`)
|
|
|
15518
15835
|
];
|
|
15519
15836
|
const out = md.join("\n");
|
|
15520
15837
|
const trimmed = out.length > maxChars ? out.slice(0, maxChars) + "\n\n...(trimmed)\n" : out;
|
|
15521
|
-
const roundtablePath =
|
|
15522
|
-
await
|
|
15838
|
+
const roundtablePath = path29.join(this.roundtableDir, `${date}.md`);
|
|
15839
|
+
await writeFile20(roundtablePath, trimmed, "utf-8");
|
|
15523
15840
|
log.info(`shared-context curated daily roundtable: ${roundtablePath}`);
|
|
15524
15841
|
return {
|
|
15525
15842
|
date,
|
|
@@ -15531,8 +15848,8 @@ ${body}`)
|
|
|
15531
15848
|
};
|
|
15532
15849
|
|
|
15533
15850
|
// src/compounding/engine.ts
|
|
15534
|
-
import { mkdir as
|
|
15535
|
-
import
|
|
15851
|
+
import { mkdir as mkdir20, readFile as readFile20, readdir as readdir15, writeFile as writeFile21 } from "fs/promises";
|
|
15852
|
+
import path30 from "path";
|
|
15536
15853
|
import os4 from "os";
|
|
15537
15854
|
function defaultTierMigrationCycleBudget(config, trigger) {
|
|
15538
15855
|
if (trigger === "extraction") {
|
|
@@ -15579,7 +15896,7 @@ function sharedContextDir(config) {
|
|
|
15579
15896
|
if (typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0) {
|
|
15580
15897
|
return config.sharedContextDir;
|
|
15581
15898
|
}
|
|
15582
|
-
return
|
|
15899
|
+
return path30.join(os4.homedir(), ".openclaw", "workspace", "shared-context");
|
|
15583
15900
|
}
|
|
15584
15901
|
function cadenceStaleWindowMs(cadence) {
|
|
15585
15902
|
switch (cadence) {
|
|
@@ -15598,16 +15915,16 @@ function cadenceStaleWindowMs(cadence) {
|
|
|
15598
15915
|
var CompoundingEngine = class {
|
|
15599
15916
|
constructor(config) {
|
|
15600
15917
|
this.config = config;
|
|
15601
|
-
this.weeklyDir =
|
|
15602
|
-
this.rubricsPath =
|
|
15603
|
-
this.mistakesPath =
|
|
15604
|
-
this.feedbackInboxPath =
|
|
15605
|
-
this.identityAnchorPath =
|
|
15606
|
-
this.identityIncidentsDir =
|
|
15607
|
-
this.identityAuditWeeklyDir =
|
|
15608
|
-
this.identityAuditMonthlyDir =
|
|
15609
|
-
this.identityImprovementLoopsPath =
|
|
15610
|
-
this.memoryActionEventsPath =
|
|
15918
|
+
this.weeklyDir = path30.join(config.memoryDir, "compounding", "weekly");
|
|
15919
|
+
this.rubricsPath = path30.join(config.memoryDir, "compounding", "rubrics.md");
|
|
15920
|
+
this.mistakesPath = path30.join(config.memoryDir, "compounding", "mistakes.json");
|
|
15921
|
+
this.feedbackInboxPath = path30.join(sharedContextDir(config), "feedback", "inbox.jsonl");
|
|
15922
|
+
this.identityAnchorPath = path30.join(config.memoryDir, "identity", "identity-anchor.md");
|
|
15923
|
+
this.identityIncidentsDir = path30.join(config.memoryDir, "identity", "incidents");
|
|
15924
|
+
this.identityAuditWeeklyDir = path30.join(config.memoryDir, "identity", "audits", "weekly");
|
|
15925
|
+
this.identityAuditMonthlyDir = path30.join(config.memoryDir, "identity", "audits", "monthly");
|
|
15926
|
+
this.identityImprovementLoopsPath = path30.join(config.memoryDir, "identity", "improvement-loops.md");
|
|
15927
|
+
this.memoryActionEventsPath = path30.join(config.memoryDir, "state", "memory-actions.jsonl");
|
|
15611
15928
|
}
|
|
15612
15929
|
weeklyDir;
|
|
15613
15930
|
rubricsPath;
|
|
@@ -15620,9 +15937,9 @@ var CompoundingEngine = class {
|
|
|
15620
15937
|
identityImprovementLoopsPath;
|
|
15621
15938
|
memoryActionEventsPath;
|
|
15622
15939
|
async ensureDirs() {
|
|
15623
|
-
await
|
|
15624
|
-
await
|
|
15625
|
-
await
|
|
15940
|
+
await mkdir20(this.weeklyDir, { recursive: true });
|
|
15941
|
+
await mkdir20(path30.dirname(this.mistakesPath), { recursive: true });
|
|
15942
|
+
await mkdir20(path30.dirname(this.rubricsPath), { recursive: true });
|
|
15626
15943
|
}
|
|
15627
15944
|
async synthesizeWeekly(opts) {
|
|
15628
15945
|
await this.ensureDirs();
|
|
@@ -15633,12 +15950,12 @@ var CompoundingEngine = class {
|
|
|
15633
15950
|
const promotionCandidates = this.config.compoundingSemanticEnabled ? this.derivePromotionCandidates(outcomeSummary) : [];
|
|
15634
15951
|
const mistakes = this.buildMistakes(entries, actionPatterns);
|
|
15635
15952
|
const continuity = this.config.continuityAuditEnabled ? await this.readContinuityAuditReferences(weekId) : { monthId: monthIdFromIsoWeek(weekId), weeklyPath: null, monthlyPath: null };
|
|
15636
|
-
const reportPath =
|
|
15953
|
+
const reportPath = path30.join(this.weeklyDir, `${weekId}.md`);
|
|
15637
15954
|
const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns, mistakes.details, continuity, outcomeSummary, promotionCandidates);
|
|
15638
|
-
await
|
|
15955
|
+
await writeFile21(reportPath, md, "utf-8");
|
|
15639
15956
|
const rubrics = this.formatRubrics(entries, outcomeSummary);
|
|
15640
|
-
await
|
|
15641
|
-
await
|
|
15957
|
+
await writeFile21(this.rubricsPath, rubrics, "utf-8");
|
|
15958
|
+
await writeFile21(
|
|
15642
15959
|
this.mistakesPath,
|
|
15643
15960
|
JSON.stringify({ updatedAt: mistakes.updatedAt, patterns: mistakes.patterns }, null, 2) + "\n",
|
|
15644
15961
|
"utf-8"
|
|
@@ -15718,9 +16035,9 @@ var CompoundingEngine = class {
|
|
|
15718
16035
|
""
|
|
15719
16036
|
];
|
|
15720
16037
|
const dir = period === "weekly" ? this.identityAuditWeeklyDir : this.identityAuditMonthlyDir;
|
|
15721
|
-
await
|
|
15722
|
-
const reportPath =
|
|
15723
|
-
await
|
|
16038
|
+
await mkdir20(dir, { recursive: true });
|
|
16039
|
+
const reportPath = path30.join(dir, `${key}.md`);
|
|
16040
|
+
await writeFile21(reportPath, lines.join("\n"), "utf-8");
|
|
15724
16041
|
return { period, key, reportPath };
|
|
15725
16042
|
}
|
|
15726
16043
|
async readMistakes() {
|
|
@@ -15819,7 +16136,7 @@ var CompoundingEngine = class {
|
|
|
15819
16136
|
else if (event.outcome === "skipped") acc.counts.skipped += 1;
|
|
15820
16137
|
else if (event.outcome === "failed") acc.counts.failed += 1;
|
|
15821
16138
|
else acc.counts.unknown += 1;
|
|
15822
|
-
acc.provenance.add(`${
|
|
16139
|
+
acc.provenance.add(`${path30.basename(this.memoryActionEventsPath)}:L${event.line}`);
|
|
15823
16140
|
byAction.set(key, acc);
|
|
15824
16141
|
}
|
|
15825
16142
|
const out = [];
|
|
@@ -15857,7 +16174,7 @@ var CompoundingEngine = class {
|
|
|
15857
16174
|
const patterns = [];
|
|
15858
16175
|
for (const wrapped of entries) {
|
|
15859
16176
|
const e = wrapped.entry;
|
|
15860
|
-
const provenance = [`${
|
|
16177
|
+
const provenance = [`${path30.basename(wrapped.sourcePath)}:L${wrapped.sourceLine}#${wrapped.entryId}`];
|
|
15861
16178
|
if (e.learning && e.learning.trim().length > 0) {
|
|
15862
16179
|
patterns.push({ pattern: `${e.agent}: ${e.learning.trim()}`, provenance });
|
|
15863
16180
|
continue;
|
|
@@ -15867,7 +16184,7 @@ var CompoundingEngine = class {
|
|
|
15867
16184
|
}
|
|
15868
16185
|
}
|
|
15869
16186
|
for (const p of actionPatterns) {
|
|
15870
|
-
patterns.push({ pattern: p, provenance: [`${
|
|
16187
|
+
patterns.push({ pattern: p, provenance: [`${path30.basename(this.memoryActionEventsPath)}:*`] });
|
|
15871
16188
|
}
|
|
15872
16189
|
const byPattern = /* @__PURE__ */ new Map();
|
|
15873
16190
|
for (const p of patterns) {
|
|
@@ -15907,7 +16224,7 @@ var CompoundingEngine = class {
|
|
|
15907
16224
|
lines.push(`- approved: ${approved}`);
|
|
15908
16225
|
lines.push(`- approved_with_feedback: ${awf}`);
|
|
15909
16226
|
lines.push(`- rejected: ${rejected}`);
|
|
15910
|
-
const provenance = list.slice(0, 3).map((e) => `${
|
|
16227
|
+
const provenance = list.slice(0, 3).map((e) => `${path30.basename(e.sourcePath)}:L${e.sourceLine}#${e.entryId}`);
|
|
15911
16228
|
if (provenance.length > 0) {
|
|
15912
16229
|
lines.push(`- provenance: ${provenance.join(", ")}`);
|
|
15913
16230
|
}
|
|
@@ -15998,7 +16315,7 @@ var CompoundingEngine = class {
|
|
|
15998
16315
|
} else {
|
|
15999
16316
|
for (const item of learnings) {
|
|
16000
16317
|
const note = (item.entry.learning && item.entry.learning.trim().length > 0 ? item.entry.learning : item.entry.reason).trim();
|
|
16001
|
-
lines.push(`- ${note} _(source: ${
|
|
16318
|
+
lines.push(`- ${note} _(source: ${path30.basename(item.sourcePath)}:L${item.sourceLine}#${item.entryId})_`);
|
|
16002
16319
|
}
|
|
16003
16320
|
}
|
|
16004
16321
|
lines.push("");
|
|
@@ -16043,7 +16360,7 @@ var CompoundingEngine = class {
|
|
|
16043
16360
|
const files = names.filter((n) => n.endsWith(".md")).sort().reverse();
|
|
16044
16361
|
for (const file of files) {
|
|
16045
16362
|
if (incidents.length >= cappedLimit) break;
|
|
16046
|
-
const filePath =
|
|
16363
|
+
const filePath = path30.join(this.identityIncidentsDir, file);
|
|
16047
16364
|
try {
|
|
16048
16365
|
const raw = await readFile20(filePath, "utf-8");
|
|
16049
16366
|
const parsed = parseContinuityIncident(raw);
|
|
@@ -16059,8 +16376,8 @@ var CompoundingEngine = class {
|
|
|
16059
16376
|
}
|
|
16060
16377
|
async readContinuityAuditReferences(weekId) {
|
|
16061
16378
|
const monthId = monthIdFromIsoWeek(weekId);
|
|
16062
|
-
const weeklyPath =
|
|
16063
|
-
const monthlyPath =
|
|
16379
|
+
const weeklyPath = path30.join(this.identityAuditWeeklyDir, `${weekId}.md`);
|
|
16380
|
+
const monthlyPath = path30.join(this.identityAuditMonthlyDir, `${monthId}.md`);
|
|
16064
16381
|
const weeklyExists = await this.readNonEmptyFile(weeklyPath);
|
|
16065
16382
|
const monthlyExists = await this.readNonEmptyFile(monthlyPath);
|
|
16066
16383
|
return {
|
|
@@ -16073,8 +16390,8 @@ var CompoundingEngine = class {
|
|
|
16073
16390
|
};
|
|
16074
16391
|
|
|
16075
16392
|
// src/tier-migration.ts
|
|
16076
|
-
import { appendFile as appendFile5, mkdir as
|
|
16077
|
-
import
|
|
16393
|
+
import { appendFile as appendFile5, mkdir as mkdir21 } from "fs/promises";
|
|
16394
|
+
import path31 from "path";
|
|
16078
16395
|
var TierMigrationExecutor = class {
|
|
16079
16396
|
storage;
|
|
16080
16397
|
qmd;
|
|
@@ -16088,7 +16405,7 @@ var TierMigrationExecutor = class {
|
|
|
16088
16405
|
this.hotCollection = options.hotCollection;
|
|
16089
16406
|
this.coldCollection = options.coldCollection;
|
|
16090
16407
|
this.autoEmbed = options.autoEmbed === true;
|
|
16091
|
-
this.journalPath = options.journalPath ??
|
|
16408
|
+
this.journalPath = options.journalPath ?? path31.join(this.storage.dir, "state", "tier-migration-journal.jsonl");
|
|
16092
16409
|
}
|
|
16093
16410
|
async migrateMemory(request) {
|
|
16094
16411
|
const { memory, fromTier, toTier, reason } = request;
|
|
@@ -16141,7 +16458,7 @@ var TierMigrationExecutor = class {
|
|
|
16141
16458
|
reason: result.reason,
|
|
16142
16459
|
targetPath: result.targetPath
|
|
16143
16460
|
};
|
|
16144
|
-
await
|
|
16461
|
+
await mkdir21(path31.dirname(this.journalPath), { recursive: true });
|
|
16145
16462
|
await appendFile5(this.journalPath, `${JSON.stringify(entry)}
|
|
16146
16463
|
`, "utf-8");
|
|
16147
16464
|
}
|
|
@@ -16308,8 +16625,8 @@ function selectRouteRule(text, rules, options) {
|
|
|
16308
16625
|
}
|
|
16309
16626
|
|
|
16310
16627
|
// src/routing/store.ts
|
|
16311
|
-
import { lstat, mkdir as
|
|
16312
|
-
import
|
|
16628
|
+
import { lstat, mkdir as mkdir22, readFile as readFile21, realpath, rename as rename2, rm as rm3, stat as stat6, writeFile as writeFile22 } from "fs/promises";
|
|
16629
|
+
import path32 from "path";
|
|
16313
16630
|
import { createHash as createHash4 } from "crypto";
|
|
16314
16631
|
function defaultState() {
|
|
16315
16632
|
return {
|
|
@@ -16328,14 +16645,14 @@ function stableRuleId(rule) {
|
|
|
16328
16645
|
return `route-${createHash4("sha256").update(seed).digest("hex").slice(0, 12)}`;
|
|
16329
16646
|
}
|
|
16330
16647
|
function resolveStatePath(memoryDir, stateFile) {
|
|
16331
|
-
const root =
|
|
16332
|
-
const defaultPath =
|
|
16333
|
-
if (
|
|
16334
|
-
const absolute =
|
|
16335
|
-
return absolute.startsWith(root +
|
|
16648
|
+
const root = path32.resolve(memoryDir);
|
|
16649
|
+
const defaultPath = path32.join(root, "state", "routing-rules.json");
|
|
16650
|
+
if (path32.isAbsolute(stateFile)) {
|
|
16651
|
+
const absolute = path32.resolve(stateFile);
|
|
16652
|
+
return absolute.startsWith(root + path32.sep) ? absolute : defaultPath;
|
|
16336
16653
|
}
|
|
16337
|
-
const resolved =
|
|
16338
|
-
return resolved.startsWith(root +
|
|
16654
|
+
const resolved = path32.resolve(root, stateFile);
|
|
16655
|
+
return resolved.startsWith(root + path32.sep) ? resolved : defaultPath;
|
|
16339
16656
|
}
|
|
16340
16657
|
function normalizeRule(rule, options) {
|
|
16341
16658
|
if (!rule || typeof rule !== "object") return null;
|
|
@@ -16368,7 +16685,7 @@ var RoutingRulesStore = class {
|
|
|
16368
16685
|
lockPath;
|
|
16369
16686
|
writeQueue = Promise.resolve();
|
|
16370
16687
|
constructor(memoryDir, stateFile = "state/routing-rules.json") {
|
|
16371
|
-
this.memoryRoot =
|
|
16688
|
+
this.memoryRoot = path32.resolve(memoryDir);
|
|
16372
16689
|
this.statePath = resolveStatePath(memoryDir, stateFile);
|
|
16373
16690
|
this.lockPath = `${this.statePath}.lock`;
|
|
16374
16691
|
}
|
|
@@ -16406,7 +16723,7 @@ var RoutingRulesStore = class {
|
|
|
16406
16723
|
await this.withWriteLock(async () => {
|
|
16407
16724
|
const payload = defaultState();
|
|
16408
16725
|
await this.assertStatePathScoped();
|
|
16409
|
-
await
|
|
16726
|
+
await writeFile22(this.statePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
16410
16727
|
});
|
|
16411
16728
|
}
|
|
16412
16729
|
dedupeById(rules) {
|
|
@@ -16440,7 +16757,7 @@ var RoutingRulesStore = class {
|
|
|
16440
16757
|
const tmpPath = `${this.statePath}.tmp-${process.pid}-${Date.now()}`;
|
|
16441
16758
|
try {
|
|
16442
16759
|
await this.assertStatePathScoped();
|
|
16443
|
-
await
|
|
16760
|
+
await writeFile22(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
16444
16761
|
await rename2(tmpPath, this.statePath);
|
|
16445
16762
|
} catch (err) {
|
|
16446
16763
|
log.debug(`routing rules write failed: ${err}`);
|
|
@@ -16474,10 +16791,10 @@ var RoutingRulesStore = class {
|
|
|
16474
16791
|
const timeoutMs = 5e3;
|
|
16475
16792
|
let unexpectedLockError = null;
|
|
16476
16793
|
await this.assertStatePathScoped();
|
|
16477
|
-
await
|
|
16794
|
+
await mkdir22(path32.dirname(this.lockPath), { recursive: true });
|
|
16478
16795
|
while (Date.now() - start < timeoutMs) {
|
|
16479
16796
|
try {
|
|
16480
|
-
await
|
|
16797
|
+
await mkdir22(this.lockPath);
|
|
16481
16798
|
return async () => {
|
|
16482
16799
|
try {
|
|
16483
16800
|
await rm3(this.lockPath, { recursive: true, force: true });
|
|
@@ -16507,14 +16824,14 @@ var RoutingRulesStore = class {
|
|
|
16507
16824
|
throw new Error(`routing rules lock acquisition timed out after ${timeoutMs}ms`);
|
|
16508
16825
|
}
|
|
16509
16826
|
async assertStatePathScoped() {
|
|
16510
|
-
await
|
|
16827
|
+
await mkdir22(this.memoryRoot, { recursive: true });
|
|
16511
16828
|
const canonicalRoot = await realpath(this.memoryRoot);
|
|
16512
|
-
const canonicalParent = await this.canonicalizePathWithoutCreating(
|
|
16513
|
-
const canonicalStatePath =
|
|
16829
|
+
const canonicalParent = await this.canonicalizePathWithoutCreating(path32.dirname(this.statePath));
|
|
16830
|
+
const canonicalStatePath = path32.join(canonicalParent, path32.basename(this.statePath));
|
|
16514
16831
|
if (!this.isPathInside(canonicalRoot, canonicalStatePath)) {
|
|
16515
16832
|
throw new Error(`routing rules state path escaped memoryDir: ${canonicalStatePath}`);
|
|
16516
16833
|
}
|
|
16517
|
-
await
|
|
16834
|
+
await mkdir22(path32.dirname(this.statePath), { recursive: true });
|
|
16518
16835
|
try {
|
|
16519
16836
|
const stateStats = await lstat(this.statePath);
|
|
16520
16837
|
if (stateStats.isSymbolicLink()) {
|
|
@@ -16531,28 +16848,28 @@ var RoutingRulesStore = class {
|
|
|
16531
16848
|
}
|
|
16532
16849
|
}
|
|
16533
16850
|
isPathInside(root, candidate) {
|
|
16534
|
-
const normalizedRoot =
|
|
16535
|
-
const normalizedCandidate =
|
|
16851
|
+
const normalizedRoot = path32.resolve(root);
|
|
16852
|
+
const normalizedCandidate = path32.resolve(candidate);
|
|
16536
16853
|
if (normalizedCandidate === normalizedRoot) return true;
|
|
16537
|
-
if (normalizedRoot ===
|
|
16854
|
+
if (normalizedRoot === path32.parse(normalizedRoot).root) {
|
|
16538
16855
|
return normalizedCandidate.startsWith(normalizedRoot);
|
|
16539
16856
|
}
|
|
16540
|
-
return normalizedCandidate.startsWith(`${normalizedRoot}${
|
|
16857
|
+
return normalizedCandidate.startsWith(`${normalizedRoot}${path32.sep}`);
|
|
16541
16858
|
}
|
|
16542
16859
|
async canonicalizePathWithoutCreating(targetPath) {
|
|
16543
|
-
const absoluteTarget =
|
|
16860
|
+
const absoluteTarget = path32.resolve(targetPath);
|
|
16544
16861
|
let probe = absoluteTarget;
|
|
16545
16862
|
while (true) {
|
|
16546
16863
|
try {
|
|
16547
16864
|
const canonicalProbe = await realpath(probe);
|
|
16548
|
-
const remainder =
|
|
16549
|
-
return
|
|
16865
|
+
const remainder = path32.relative(probe, absoluteTarget);
|
|
16866
|
+
return path32.resolve(canonicalProbe, remainder);
|
|
16550
16867
|
} catch (err) {
|
|
16551
16868
|
const code = err.code;
|
|
16552
16869
|
if (code !== "ENOENT") {
|
|
16553
16870
|
throw err;
|
|
16554
16871
|
}
|
|
16555
|
-
const parent =
|
|
16872
|
+
const parent = path32.dirname(probe);
|
|
16556
16873
|
if (parent === probe) {
|
|
16557
16874
|
return absoluteTarget;
|
|
16558
16875
|
}
|
|
@@ -16563,8 +16880,8 @@ var RoutingRulesStore = class {
|
|
|
16563
16880
|
};
|
|
16564
16881
|
|
|
16565
16882
|
// src/policy-runtime.ts
|
|
16566
|
-
import
|
|
16567
|
-
import { mkdir as
|
|
16883
|
+
import path33 from "path";
|
|
16884
|
+
import { mkdir as mkdir23, readFile as readFile22, rename as rename3, writeFile as writeFile23 } from "fs/promises";
|
|
16568
16885
|
var RUNTIME_POLICY_VERSION = 1;
|
|
16569
16886
|
var RUNTIME_POLICY_FILE = "policy-runtime.json";
|
|
16570
16887
|
var RUNTIME_POLICY_PREV_FILE = "policy-runtime.prev.json";
|
|
@@ -16612,8 +16929,8 @@ async function readRuntimePolicySnapshot(filePath, options) {
|
|
|
16612
16929
|
}
|
|
16613
16930
|
async function writeSnapshotAtomic(filePath, snapshot) {
|
|
16614
16931
|
const tempPath = `${filePath}.tmp`;
|
|
16615
|
-
await
|
|
16616
|
-
await
|
|
16932
|
+
await mkdir23(path33.dirname(filePath), { recursive: true });
|
|
16933
|
+
await writeFile23(tempPath, `${JSON.stringify(snapshot, null, 2)}
|
|
16617
16934
|
`, "utf-8");
|
|
16618
16935
|
await rename3(tempPath, filePath);
|
|
16619
16936
|
}
|
|
@@ -16621,9 +16938,9 @@ var PolicyRuntimeManager = class {
|
|
|
16621
16938
|
constructor(memoryDir, config) {
|
|
16622
16939
|
this.memoryDir = memoryDir;
|
|
16623
16940
|
this.config = config;
|
|
16624
|
-
const stateDir2 =
|
|
16625
|
-
this.runtimePath =
|
|
16626
|
-
this.runtimePrevPath =
|
|
16941
|
+
const stateDir2 = path33.join(memoryDir, "state");
|
|
16942
|
+
this.runtimePath = path33.join(stateDir2, RUNTIME_POLICY_FILE);
|
|
16943
|
+
this.runtimePrevPath = path33.join(stateDir2, RUNTIME_POLICY_PREV_FILE);
|
|
16627
16944
|
}
|
|
16628
16945
|
runtimePath;
|
|
16629
16946
|
runtimePrevPath;
|
|
@@ -16776,7 +17093,7 @@ function dedupeBehaviorSignalsByMemoryAndHash(signals) {
|
|
|
16776
17093
|
// src/orchestrator.ts
|
|
16777
17094
|
var COMPACTION_SIGNAL_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
16778
17095
|
function defaultWorkspaceDir() {
|
|
16779
|
-
return
|
|
17096
|
+
return path34.join(os5.homedir(), ".openclaw", "workspace");
|
|
16780
17097
|
}
|
|
16781
17098
|
function sanitizeSessionKeyForFilename(sessionKey) {
|
|
16782
17099
|
const readable = sessionKey.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
@@ -16916,11 +17233,11 @@ function mergeGraphExpandedResults(primary, expanded) {
|
|
|
16916
17233
|
return Array.from(mergedByPath.values());
|
|
16917
17234
|
}
|
|
16918
17235
|
function graphPathRelativeToStorage(storageDir, candidatePath) {
|
|
16919
|
-
const absolutePath =
|
|
16920
|
-
const rel =
|
|
17236
|
+
const absolutePath = path34.isAbsolute(candidatePath) ? candidatePath : path34.resolve(storageDir, candidatePath);
|
|
17237
|
+
const rel = path34.relative(storageDir, absolutePath);
|
|
16921
17238
|
if (!rel || rel === ".") return null;
|
|
16922
17239
|
if (rel.startsWith("..")) return null;
|
|
16923
|
-
return rel.split(
|
|
17240
|
+
return rel.split(path34.sep).join("/");
|
|
16924
17241
|
}
|
|
16925
17242
|
function normalizeGraphActivationScore(score) {
|
|
16926
17243
|
const bounded = Number.isFinite(score) && score > 0 ? score : 0;
|
|
@@ -16995,7 +17312,7 @@ function buildMemoryPathById(allMemsForGraph, storageDir) {
|
|
|
16995
17312
|
for (const mem of allMemsForGraph ?? []) {
|
|
16996
17313
|
const id = mem.frontmatter.id;
|
|
16997
17314
|
if (!id) continue;
|
|
16998
|
-
pathById.set(id,
|
|
17315
|
+
pathById.set(id, path34.relative(storageDir, mem.path));
|
|
16999
17316
|
}
|
|
17000
17317
|
return pathById;
|
|
17001
17318
|
}
|
|
@@ -17003,7 +17320,7 @@ function appendMemoryToGraphContext(options) {
|
|
|
17003
17320
|
if (!Array.isArray(options.allMemsForGraph)) return;
|
|
17004
17321
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
17005
17322
|
options.allMemsForGraph.push({
|
|
17006
|
-
path:
|
|
17323
|
+
path: path34.join(options.storageDir, options.memoryRelPath),
|
|
17007
17324
|
content: options.content,
|
|
17008
17325
|
frontmatter: {
|
|
17009
17326
|
id: options.memoryId,
|
|
@@ -17023,15 +17340,15 @@ function resolvePersistedMemoryRelativePath(options) {
|
|
|
17023
17340
|
const persisted = options.pathById.get(options.memoryId);
|
|
17024
17341
|
if (persisted) return persisted;
|
|
17025
17342
|
if (options.category === "correction") {
|
|
17026
|
-
return
|
|
17343
|
+
return path34.join("corrections", `${options.memoryId}.md`);
|
|
17027
17344
|
}
|
|
17028
17345
|
const idParts = options.memoryId.split("-");
|
|
17029
17346
|
const maybeTimestamp = Number(idParts[1]);
|
|
17030
17347
|
if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
|
|
17031
17348
|
const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
|
|
17032
|
-
return
|
|
17349
|
+
return path34.join("facts", day, `${options.memoryId}.md`);
|
|
17033
17350
|
}
|
|
17034
|
-
return
|
|
17351
|
+
return path34.join("facts", `${options.memoryId}.md`);
|
|
17035
17352
|
}
|
|
17036
17353
|
function shouldRejectLowConfidenceRecall(results, threshold) {
|
|
17037
17354
|
if (results.length === 0) return false;
|
|
@@ -17139,7 +17456,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17139
17456
|
this.compounding = config.compoundingEnabled ? new CompoundingEngine(config) : void 0;
|
|
17140
17457
|
this.buffer = new SmartBuffer(config, this.storage);
|
|
17141
17458
|
this.transcript = new TranscriptManager(config);
|
|
17142
|
-
this.conversationIndexDir =
|
|
17459
|
+
this.conversationIndexDir = path34.join(config.memoryDir, "conversation-index", "chunks");
|
|
17143
17460
|
this.modelRegistry = new ModelRegistry(config.memoryDir);
|
|
17144
17461
|
this.relevance = new RelevanceStore(config.memoryDir);
|
|
17145
17462
|
this.negatives = new NegativeExampleStore(config.memoryDir);
|
|
@@ -17164,7 +17481,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17164
17481
|
})() : this.localLlm;
|
|
17165
17482
|
this.extraction = new ExtractionEngine(config, this.localLlm, config.gatewayConfig, this.modelRegistry);
|
|
17166
17483
|
this.threading = new ThreadingManager(
|
|
17167
|
-
|
|
17484
|
+
path34.join(config.memoryDir, "threads"),
|
|
17168
17485
|
config.threadingGapMinutes
|
|
17169
17486
|
);
|
|
17170
17487
|
this.tmtBuilder = new TmtBuilder(config.memoryDir, {
|
|
@@ -17333,7 +17650,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17333
17650
|
await this.sessionObserver.load();
|
|
17334
17651
|
this.runtimePolicyValues = await this.policyRuntime.loadRuntimeValues();
|
|
17335
17652
|
if (this.config.factDeduplicationEnabled) {
|
|
17336
|
-
const stateDir2 =
|
|
17653
|
+
const stateDir2 = path34.join(this.config.memoryDir, "state");
|
|
17337
17654
|
this.contentHashIndex = new ContentHashIndex(stateDir2);
|
|
17338
17655
|
await this.contentHashIndex.load();
|
|
17339
17656
|
log.info(`content-hash dedup: loaded ${this.contentHashIndex.size} hashes`);
|
|
@@ -17372,7 +17689,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17372
17689
|
if (available) {
|
|
17373
17690
|
log.info(`Conversation index QMD: available ${this.conversationQmd.debugStatus()}`);
|
|
17374
17691
|
const collectionState = await this.conversationQmd.ensureCollection(
|
|
17375
|
-
|
|
17692
|
+
path34.join(this.config.memoryDir, "conversation-index")
|
|
17376
17693
|
);
|
|
17377
17694
|
if (collectionState === "missing") {
|
|
17378
17695
|
this.config.conversationIndexEnabled = false;
|
|
@@ -17408,7 +17725,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17408
17725
|
const files = await readdir16(wsDir).catch(() => []);
|
|
17409
17726
|
for (const f of files) {
|
|
17410
17727
|
if (!f.startsWith(".compaction-reset-signal-")) continue;
|
|
17411
|
-
const fp =
|
|
17728
|
+
const fp = path34.join(wsDir, f);
|
|
17412
17729
|
const s = await stat7(fp).catch(() => null);
|
|
17413
17730
|
if (s && Date.now() - s.mtimeMs >= COMPACTION_SIGNAL_MAX_AGE_MS) {
|
|
17414
17731
|
await unlink5(fp).catch(() => {
|
|
@@ -17444,12 +17761,12 @@ var Orchestrator = class _Orchestrator {
|
|
|
17444
17761
|
this.lastFileHygieneRunAtMs = now;
|
|
17445
17762
|
if (hygiene.rotateEnabled) {
|
|
17446
17763
|
for (const rel of hygiene.rotatePaths) {
|
|
17447
|
-
const abs =
|
|
17764
|
+
const abs = path34.isAbsolute(rel) ? rel : path34.join(this.config.workspaceDir, rel);
|
|
17448
17765
|
try {
|
|
17449
17766
|
const raw = await readFile23(abs, "utf-8");
|
|
17450
17767
|
if (raw.length > hygiene.rotateMaxBytes) {
|
|
17451
|
-
const archiveDir =
|
|
17452
|
-
const base =
|
|
17768
|
+
const archiveDir = path34.join(this.config.workspaceDir, hygiene.archiveDir);
|
|
17769
|
+
const base = path34.basename(abs);
|
|
17453
17770
|
const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
|
|
17454
17771
|
const { newContent } = await rotateMarkdownFileToArchive({
|
|
17455
17772
|
filePath: abs,
|
|
@@ -17457,7 +17774,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17457
17774
|
archivePrefix: prefix,
|
|
17458
17775
|
keepTailChars: hygiene.rotateKeepTailChars
|
|
17459
17776
|
});
|
|
17460
|
-
await
|
|
17777
|
+
await writeFile24(abs, newContent, "utf-8");
|
|
17461
17778
|
}
|
|
17462
17779
|
} catch {
|
|
17463
17780
|
}
|
|
@@ -17474,8 +17791,8 @@ var Orchestrator = class _Orchestrator {
|
|
|
17474
17791
|
log.warn(w.message);
|
|
17475
17792
|
}
|
|
17476
17793
|
if (hygiene.warningsLogEnabled && warnings.length > 0) {
|
|
17477
|
-
const fp =
|
|
17478
|
-
await
|
|
17794
|
+
const fp = path34.join(this.config.memoryDir, hygiene.warningsLogPath);
|
|
17795
|
+
await mkdir24(path34.dirname(fp), { recursive: true });
|
|
17479
17796
|
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
17480
17797
|
const block = `
|
|
17481
17798
|
|
|
@@ -17488,7 +17805,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17488
17805
|
} catch {
|
|
17489
17806
|
existing = "# Engram File Hygiene Warnings\n";
|
|
17490
17807
|
}
|
|
17491
|
-
await
|
|
17808
|
+
await writeFile24(fp, existing + block, "utf-8");
|
|
17492
17809
|
}
|
|
17493
17810
|
}
|
|
17494
17811
|
}
|
|
@@ -17564,7 +17881,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
17564
17881
|
}
|
|
17565
17882
|
async getLastGraphRecallSnapshot(namespace) {
|
|
17566
17883
|
const storage = await this.getStorage(namespace);
|
|
17567
|
-
const snapshotPath =
|
|
17884
|
+
const snapshotPath = path34.join(storage.dir, "state", "last_graph_recall.json");
|
|
17568
17885
|
try {
|
|
17569
17886
|
const raw = await readFile23(snapshotPath, "utf-8");
|
|
17570
17887
|
const parsed = JSON.parse(raw);
|
|
@@ -17635,7 +17952,7 @@ ${r.snippet.trim()}
|
|
|
17635
17952
|
const entries = await readdir16(dir, { withFileTypes: true });
|
|
17636
17953
|
let total = 0;
|
|
17637
17954
|
for (const entry of entries) {
|
|
17638
|
-
const fullPath =
|
|
17955
|
+
const fullPath = path34.join(dir, entry.name);
|
|
17639
17956
|
if (entry.isDirectory()) {
|
|
17640
17957
|
total += await this.countConversationChunkDocs(fullPath);
|
|
17641
17958
|
continue;
|
|
@@ -17960,7 +18277,7 @@ ${r.snippet.trim()}
|
|
|
17960
18277
|
const seedRelativePaths = seedCandidates.map((result) => graphPathRelativeToStorage(storage.dir, result.path)).filter((value) => typeof value === "string" && value.length > 0);
|
|
17961
18278
|
if (seedRelativePaths.length === 0) continue;
|
|
17962
18279
|
const seedRecallScore = seedCandidates.reduce((max, item) => Math.max(max, item.score), 0);
|
|
17963
|
-
seedPaths.push(...seedRelativePaths.map((rel) =>
|
|
18280
|
+
seedPaths.push(...seedRelativePaths.map((rel) => path34.join(storage.dir, rel)));
|
|
17964
18281
|
const seedSet = new Set(seedRelativePaths);
|
|
17965
18282
|
const expanded = await this.graphIndexFor(storage).spreadingActivation(
|
|
17966
18283
|
seedRelativePaths,
|
|
@@ -17969,7 +18286,7 @@ ${r.snippet.trim()}
|
|
|
17969
18286
|
if (expanded.length === 0) continue;
|
|
17970
18287
|
for (const candidate of expanded.slice(0, perNamespaceExpandedCap)) {
|
|
17971
18288
|
if (seedSet.has(candidate.path)) continue;
|
|
17972
|
-
const memoryPath =
|
|
18289
|
+
const memoryPath = path34.resolve(storage.dir, candidate.path);
|
|
17973
18290
|
const memory = await storage.readMemoryByPath(memoryPath);
|
|
17974
18291
|
if (!memory) continue;
|
|
17975
18292
|
if (isArtifactMemoryPath(memory.path)) continue;
|
|
@@ -17992,7 +18309,7 @@ ${r.snippet.trim()}
|
|
|
17992
18309
|
path: memory.path,
|
|
17993
18310
|
score,
|
|
17994
18311
|
namespace,
|
|
17995
|
-
seed:
|
|
18312
|
+
seed: path34.resolve(storage.dir, candidate.seed),
|
|
17996
18313
|
hopDepth: candidate.hopDepth,
|
|
17997
18314
|
decayedWeight: candidate.decayedWeight,
|
|
17998
18315
|
graphType: candidate.graphType
|
|
@@ -18007,8 +18324,8 @@ ${r.snippet.trim()}
|
|
|
18007
18324
|
}
|
|
18008
18325
|
async recordLastGraphRecallSnapshot(options) {
|
|
18009
18326
|
try {
|
|
18010
|
-
const snapshotPath =
|
|
18011
|
-
await
|
|
18327
|
+
const snapshotPath = path34.join(options.storage.dir, "state", "last_graph_recall.json");
|
|
18328
|
+
await mkdir24(path34.dirname(snapshotPath), { recursive: true });
|
|
18012
18329
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18013
18330
|
const totalSeedCount = options.seedPaths.length;
|
|
18014
18331
|
const totalExpandedCount = options.expandedPaths.length;
|
|
@@ -18025,7 +18342,7 @@ ${r.snippet.trim()}
|
|
|
18025
18342
|
seeds,
|
|
18026
18343
|
expanded
|
|
18027
18344
|
};
|
|
18028
|
-
await
|
|
18345
|
+
await writeFile24(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
18029
18346
|
} catch (err) {
|
|
18030
18347
|
log.debug(`last graph recall write failed: ${err}`);
|
|
18031
18348
|
}
|
|
@@ -18313,6 +18630,27 @@ ${r.snippet.trim()}
|
|
|
18313
18630
|
timings.causalTrajectories = `${Date.now() - t0}ms`;
|
|
18314
18631
|
return results.length > 0 ? this.formatCausalTrajectoryResults(results) : null;
|
|
18315
18632
|
})();
|
|
18633
|
+
const trustZonePromise = (async () => {
|
|
18634
|
+
const t0 = Date.now();
|
|
18635
|
+
if (!this.config.trustZonesEnabled || !this.config.trustZoneRecallEnabled || !this.isRecallSectionEnabled("trust-zones", this.config.trustZoneRecallEnabled === true)) {
|
|
18636
|
+
timings.trustZones = "skip";
|
|
18637
|
+
return null;
|
|
18638
|
+
}
|
|
18639
|
+
const maxResults = this.getRecallSectionNumber("trust-zones", "maxResults") ?? 3;
|
|
18640
|
+
if (maxResults <= 0) {
|
|
18641
|
+
timings.trustZones = "skip(limit=0)";
|
|
18642
|
+
return null;
|
|
18643
|
+
}
|
|
18644
|
+
const results = await searchTrustZoneRecords({
|
|
18645
|
+
memoryDir: this.config.memoryDir,
|
|
18646
|
+
trustZoneStoreDir: this.config.trustZoneStoreDir,
|
|
18647
|
+
query: retrievalQuery,
|
|
18648
|
+
maxResults,
|
|
18649
|
+
sessionKey
|
|
18650
|
+
});
|
|
18651
|
+
timings.trustZones = `${Date.now() - t0}ms`;
|
|
18652
|
+
return results.length > 0 ? this.formatTrustZoneResults(results) : null;
|
|
18653
|
+
})();
|
|
18316
18654
|
const qmdPromise = (async () => {
|
|
18317
18655
|
if (recallResultLimit <= 0) {
|
|
18318
18656
|
timings.qmd = "skip(limit=0)";
|
|
@@ -18386,8 +18724,8 @@ ${formatted}`;
|
|
|
18386
18724
|
if (!this.config.compactionResetEnabled) return null;
|
|
18387
18725
|
const workspaceDir = compactionWorkspaceDir || this.config.workspaceDir || defaultWorkspaceDir();
|
|
18388
18726
|
const safeSessionKey = sanitizeSessionKeyForFilename(effectiveSessionKey);
|
|
18389
|
-
const signalPath =
|
|
18390
|
-
const bootPath =
|
|
18727
|
+
const signalPath = path34.join(workspaceDir, `.compaction-reset-signal-${safeSessionKey}`);
|
|
18728
|
+
const bootPath = path34.join(workspaceDir, "BOOT.md");
|
|
18391
18729
|
try {
|
|
18392
18730
|
const signalStat = await stat7(signalPath).catch(() => null);
|
|
18393
18731
|
if (!signalStat) return null;
|
|
@@ -18518,6 +18856,7 @@ ${formatted}`;
|
|
|
18518
18856
|
artifacts,
|
|
18519
18857
|
objectiveStateSection,
|
|
18520
18858
|
causalTrajectorySection,
|
|
18859
|
+
trustZoneSection,
|
|
18521
18860
|
qmdResult,
|
|
18522
18861
|
transcriptSection,
|
|
18523
18862
|
compactionSection,
|
|
@@ -18532,6 +18871,7 @@ ${formatted}`;
|
|
|
18532
18871
|
artifactsPromise,
|
|
18533
18872
|
objectiveStatePromise,
|
|
18534
18873
|
causalTrajectoryPromise,
|
|
18874
|
+
trustZonePromise,
|
|
18535
18875
|
qmdPromise,
|
|
18536
18876
|
transcriptPromise,
|
|
18537
18877
|
compactionPromise,
|
|
@@ -18592,6 +18932,9 @@ ${tmtNode.summary}`);
|
|
|
18592
18932
|
if (causalTrajectorySection) {
|
|
18593
18933
|
this.appendRecallSection(sectionBuckets, "causal-trajectories", causalTrajectorySection);
|
|
18594
18934
|
}
|
|
18935
|
+
if (trustZoneSection) {
|
|
18936
|
+
this.appendRecallSection(sectionBuckets, "trust-zones", trustZoneSection);
|
|
18937
|
+
}
|
|
18595
18938
|
if (qmdResult) {
|
|
18596
18939
|
const t0 = Date.now();
|
|
18597
18940
|
const { memoryResultsLists, globalResults } = qmdResult;
|
|
@@ -19385,7 +19728,7 @@ _Context: ${topQuestion.context}_`
|
|
|
19385
19728
|
};
|
|
19386
19729
|
this.tierMigrationInFlight = true;
|
|
19387
19730
|
try {
|
|
19388
|
-
const coldStorage = new StorageManager(
|
|
19731
|
+
const coldStorage = new StorageManager(path34.join(storage.dir, "cold"));
|
|
19389
19732
|
const [hotMemories, coldMemories] = await Promise.all([
|
|
19390
19733
|
storage.readAllMemories(),
|
|
19391
19734
|
coldStorage.readAllMemories()
|
|
@@ -19979,7 +20322,7 @@ _Context: ${topQuestion.context}_`
|
|
|
19979
20322
|
const allMems = allMemsForGraph ?? [];
|
|
19980
20323
|
for (const m of allMems) {
|
|
19981
20324
|
if (m.frontmatter.entityRef === entityRef) {
|
|
19982
|
-
const rel =
|
|
20325
|
+
const rel = path34.relative(storage.dir, m.path);
|
|
19983
20326
|
if (rel !== memoryRelPath) entitySiblings.push(rel);
|
|
19984
20327
|
}
|
|
19985
20328
|
}
|
|
@@ -20543,9 +20886,9 @@ ${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
|
|
|
20543
20886
|
protectedCategories: this.config.lifecycleProtectedCategories
|
|
20544
20887
|
}
|
|
20545
20888
|
};
|
|
20546
|
-
const metricsPath =
|
|
20547
|
-
await
|
|
20548
|
-
await
|
|
20889
|
+
const metricsPath = path34.join(this.storage.dir, "state", "lifecycle-metrics.json");
|
|
20890
|
+
await mkdir24(path34.dirname(metricsPath), { recursive: true });
|
|
20891
|
+
await writeFile24(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
|
|
20549
20892
|
}
|
|
20550
20893
|
/**
|
|
20551
20894
|
* Archive old, low-importance, rarely-accessed facts (v6.0).
|
|
@@ -20740,6 +21083,33 @@ ${details.join("\n")}`;
|
|
|
20740
21083
|
});
|
|
20741
21084
|
return `## Causal Trajectories
|
|
20742
21085
|
|
|
21086
|
+
${lines.join("\n\n")}`;
|
|
21087
|
+
}
|
|
21088
|
+
formatTrustZoneResults(results) {
|
|
21089
|
+
const lines = results.map(({ record, matchedFields }, index) => {
|
|
21090
|
+
const header = [
|
|
21091
|
+
`[${index + 1}] ${record.recordedAt.replace("T", " ").slice(0, 16)}`,
|
|
21092
|
+
record.zone,
|
|
21093
|
+
record.kind
|
|
21094
|
+
].join(" | ");
|
|
21095
|
+
const details = [
|
|
21096
|
+
record.summary,
|
|
21097
|
+
`provenance: ${record.provenance.sourceClass}`
|
|
21098
|
+
];
|
|
21099
|
+
if (record.entityRefs && record.entityRefs.length > 0) {
|
|
21100
|
+
details.push(`entities: ${record.entityRefs.join(", ")}`);
|
|
21101
|
+
}
|
|
21102
|
+
if (record.tags && record.tags.length > 0) {
|
|
21103
|
+
details.push(`tags: ${record.tags.join(", ")}`);
|
|
21104
|
+
}
|
|
21105
|
+
if (matchedFields.length > 0) {
|
|
21106
|
+
details.push(`matched: ${matchedFields.join(", ")}`);
|
|
21107
|
+
}
|
|
21108
|
+
return `${header}
|
|
21109
|
+
${details.join("\n")}`;
|
|
21110
|
+
});
|
|
21111
|
+
return `## Trust Zones
|
|
21112
|
+
|
|
20743
21113
|
${lines.join("\n\n")}`;
|
|
20744
21114
|
}
|
|
20745
21115
|
summarizeIdentityText(raw, maxLines, maxChars) {
|
|
@@ -20873,7 +21243,7 @@ ${lines.join("\n\n")}`;
|
|
|
20873
21243
|
if (hits.length === 0) return [];
|
|
20874
21244
|
const results = [];
|
|
20875
21245
|
for (const hit of hits) {
|
|
20876
|
-
const fullPath =
|
|
21246
|
+
const fullPath = path34.isAbsolute(hit.path) ? hit.path : path34.join(this.config.memoryDir, hit.path);
|
|
20877
21247
|
const memory = await this.storage.readMemoryByPath(fullPath);
|
|
20878
21248
|
if (!memory) continue;
|
|
20879
21249
|
results.push({
|
|
@@ -21331,8 +21701,8 @@ ${lines.join("\n\n")}`;
|
|
|
21331
21701
|
}
|
|
21332
21702
|
namespaceFromStorageDir(storageDir) {
|
|
21333
21703
|
if (!this.config.namespacesEnabled) return this.config.defaultNamespace;
|
|
21334
|
-
const resolvedStorageDir =
|
|
21335
|
-
const resolvedMemoryDir =
|
|
21704
|
+
const resolvedStorageDir = path34.resolve(storageDir);
|
|
21705
|
+
const resolvedMemoryDir = path34.resolve(this.config.memoryDir);
|
|
21336
21706
|
if (resolvedStorageDir === resolvedMemoryDir) return this.config.defaultNamespace;
|
|
21337
21707
|
const m = resolvedStorageDir.match(/[\\/]namespaces[\\/]([^\\/]+)$/);
|
|
21338
21708
|
return m && m[1] ? m[1] : this.config.defaultNamespace;
|
|
@@ -21360,14 +21730,14 @@ ${lines.join("\n\n")}`;
|
|
|
21360
21730
|
};
|
|
21361
21731
|
|
|
21362
21732
|
// src/tools.ts
|
|
21363
|
-
import
|
|
21733
|
+
import path36 from "path";
|
|
21364
21734
|
import { createHash as createHash7 } from "crypto";
|
|
21365
21735
|
import { Type } from "@sinclair/typebox";
|
|
21366
21736
|
|
|
21367
21737
|
// src/work/storage.ts
|
|
21368
|
-
import
|
|
21738
|
+
import path35 from "path";
|
|
21369
21739
|
import { randomUUID } from "crypto";
|
|
21370
|
-
import { mkdir as
|
|
21740
|
+
import { mkdir as mkdir25, readdir as readdir17, readFile as readFile24, rm as rm4, writeFile as writeFile25 } from "fs/promises";
|
|
21371
21741
|
var TASK_TRANSITIONS = {
|
|
21372
21742
|
todo: /* @__PURE__ */ new Set(["in_progress", "blocked", "cancelled"]),
|
|
21373
21743
|
in_progress: /* @__PURE__ */ new Set(["todo", "blocked", "done", "cancelled"]),
|
|
@@ -21442,22 +21812,22 @@ function ensureProjectStatus(value) {
|
|
|
21442
21812
|
var WorkStorage = class {
|
|
21443
21813
|
constructor(memoryDir) {
|
|
21444
21814
|
this.memoryDir = memoryDir;
|
|
21445
|
-
this.tasksDir =
|
|
21446
|
-
this.projectsDir =
|
|
21815
|
+
this.tasksDir = path35.join(memoryDir, "work", "tasks");
|
|
21816
|
+
this.projectsDir = path35.join(memoryDir, "work", "projects");
|
|
21447
21817
|
}
|
|
21448
21818
|
tasksDir;
|
|
21449
21819
|
projectsDir;
|
|
21450
21820
|
async ensureDirectories() {
|
|
21451
|
-
await
|
|
21452
|
-
await
|
|
21821
|
+
await mkdir25(this.tasksDir, { recursive: true });
|
|
21822
|
+
await mkdir25(this.projectsDir, { recursive: true });
|
|
21453
21823
|
}
|
|
21454
21824
|
taskPath(id) {
|
|
21455
21825
|
assertValidWorkId(id, "task");
|
|
21456
|
-
return
|
|
21826
|
+
return path35.join(this.tasksDir, `${id}.md`);
|
|
21457
21827
|
}
|
|
21458
21828
|
projectPath(id) {
|
|
21459
21829
|
assertValidWorkId(id, "project");
|
|
21460
|
-
return
|
|
21830
|
+
return path35.join(this.projectsDir, `${id}.md`);
|
|
21461
21831
|
}
|
|
21462
21832
|
serializeTask(task) {
|
|
21463
21833
|
return `${serializeFrontmatter2(task)}
|
|
@@ -21529,7 +21899,7 @@ ${project.description}
|
|
|
21529
21899
|
throw new Error(`project not found: ${task.projectId}`);
|
|
21530
21900
|
}
|
|
21531
21901
|
}
|
|
21532
|
-
await
|
|
21902
|
+
await writeFile25(this.taskPath(task.id), this.serializeTask(task), "utf-8");
|
|
21533
21903
|
if (task.projectId) {
|
|
21534
21904
|
await this.addTaskIdToProject(task.projectId, task.id, now);
|
|
21535
21905
|
}
|
|
@@ -21549,7 +21919,7 @@ ${project.description}
|
|
|
21549
21919
|
const out = [];
|
|
21550
21920
|
for (const entry of entries) {
|
|
21551
21921
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
21552
|
-
const raw = await readFile24(
|
|
21922
|
+
const raw = await readFile24(path35.join(this.tasksDir, entry.name), "utf-8");
|
|
21553
21923
|
const task = this.parseTask(raw);
|
|
21554
21924
|
if (!task) continue;
|
|
21555
21925
|
if (filter?.status && task.status !== filter.status) continue;
|
|
@@ -21591,7 +21961,7 @@ ${project.description}
|
|
|
21591
21961
|
tags: patch.tags ?? existing.tags,
|
|
21592
21962
|
updatedAt: now.toISOString()
|
|
21593
21963
|
};
|
|
21594
|
-
await
|
|
21964
|
+
await writeFile25(this.taskPath(id), this.serializeTask(next), "utf-8");
|
|
21595
21965
|
return next;
|
|
21596
21966
|
}
|
|
21597
21967
|
async transitionTask(id, nextStatus, now = /* @__PURE__ */ new Date()) {
|
|
@@ -21631,7 +22001,7 @@ ${project.description}
|
|
|
21631
22001
|
createdAt: timestamp,
|
|
21632
22002
|
updatedAt: timestamp
|
|
21633
22003
|
};
|
|
21634
|
-
await
|
|
22004
|
+
await writeFile25(this.projectPath(project.id), this.serializeProject(project), "utf-8");
|
|
21635
22005
|
return project;
|
|
21636
22006
|
}
|
|
21637
22007
|
async getProject(id) {
|
|
@@ -21648,7 +22018,7 @@ ${project.description}
|
|
|
21648
22018
|
const out = [];
|
|
21649
22019
|
for (const entry of entries) {
|
|
21650
22020
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
21651
|
-
const raw = await readFile24(
|
|
22021
|
+
const raw = await readFile24(path35.join(this.projectsDir, entry.name), "utf-8");
|
|
21652
22022
|
const project = this.parseProject(raw);
|
|
21653
22023
|
if (project) out.push(project);
|
|
21654
22024
|
}
|
|
@@ -21665,7 +22035,7 @@ ${project.description}
|
|
|
21665
22035
|
taskIds: patch.taskIds ? [...patch.taskIds].sort() : existing.taskIds,
|
|
21666
22036
|
updatedAt: now.toISOString()
|
|
21667
22037
|
};
|
|
21668
|
-
await
|
|
22038
|
+
await writeFile25(this.projectPath(id), this.serializeProject(next), "utf-8");
|
|
21669
22039
|
return next;
|
|
21670
22040
|
}
|
|
21671
22041
|
async deleteProject(id) {
|
|
@@ -23204,7 +23574,7 @@ Best for:
|
|
|
23204
23574
|
- Reviewing identity development over time`,
|
|
23205
23575
|
parameters: Type.Object({}),
|
|
23206
23576
|
async execute() {
|
|
23207
|
-
const workspaceDir =
|
|
23577
|
+
const workspaceDir = path36.join(process.env.HOME ?? "~", ".openclaw", "workspace");
|
|
23208
23578
|
const identity = await orchestrator.storage.readIdentity(workspaceDir);
|
|
23209
23579
|
if (!identity) {
|
|
23210
23580
|
return toolResult("No identity file found. Identity reflections build automatically through conversations when identityEnabled is true.");
|
|
@@ -23726,8 +24096,8 @@ import { access as access3, readFile as readFile37, readdir as readdir24, unlink
|
|
|
23726
24096
|
import { createHash as createHash10 } from "crypto";
|
|
23727
24097
|
|
|
23728
24098
|
// src/transfer/export-json.ts
|
|
23729
|
-
import
|
|
23730
|
-
import { mkdir as
|
|
24099
|
+
import path38 from "path";
|
|
24100
|
+
import { mkdir as mkdir27, readFile as readFile26 } from "fs/promises";
|
|
23731
24101
|
|
|
23732
24102
|
// src/transfer/constants.ts
|
|
23733
24103
|
var EXPORT_FORMAT = "openclaw-engram-export";
|
|
@@ -23735,8 +24105,8 @@ var EXPORT_SCHEMA_VERSION = 1;
|
|
|
23735
24105
|
|
|
23736
24106
|
// src/transfer/fs-utils.ts
|
|
23737
24107
|
import { createHash as createHash8 } from "crypto";
|
|
23738
|
-
import { mkdir as
|
|
23739
|
-
import
|
|
24108
|
+
import { mkdir as mkdir26, readdir as readdir18, readFile as readFile25, stat as stat8, writeFile as writeFile26 } from "fs/promises";
|
|
24109
|
+
import path37 from "path";
|
|
23740
24110
|
async function sha256File(filePath) {
|
|
23741
24111
|
const buf = await readFile25(filePath);
|
|
23742
24112
|
const sha256 = createHash8("sha256").update(buf).digest("hex");
|
|
@@ -23748,8 +24118,8 @@ function sha256String(content) {
|
|
|
23748
24118
|
return { sha256, bytes: buf.byteLength };
|
|
23749
24119
|
}
|
|
23750
24120
|
async function writeJsonFile(filePath, value) {
|
|
23751
|
-
await
|
|
23752
|
-
await
|
|
24121
|
+
await mkdir26(path37.dirname(filePath), { recursive: true });
|
|
24122
|
+
await writeFile26(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
23753
24123
|
}
|
|
23754
24124
|
async function readJsonFile2(filePath) {
|
|
23755
24125
|
const raw = await readFile25(filePath, "utf-8");
|
|
@@ -23760,7 +24130,7 @@ async function listFilesRecursive(rootDir) {
|
|
|
23760
24130
|
async function walk(dir) {
|
|
23761
24131
|
const entries = await readdir18(dir, { withFileTypes: true });
|
|
23762
24132
|
for (const ent of entries) {
|
|
23763
|
-
const fp =
|
|
24133
|
+
const fp = path37.join(dir, ent.name);
|
|
23764
24134
|
if (ent.isDirectory()) {
|
|
23765
24135
|
await walk(fp);
|
|
23766
24136
|
} else if (ent.isFile()) {
|
|
@@ -23780,11 +24150,11 @@ async function fileExists(filePath) {
|
|
|
23780
24150
|
}
|
|
23781
24151
|
}
|
|
23782
24152
|
function toPosixRelPath(absPath, rootDir) {
|
|
23783
|
-
const rel =
|
|
23784
|
-
return rel.split(
|
|
24153
|
+
const rel = path37.relative(rootDir, absPath);
|
|
24154
|
+
return rel.split(path37.sep).join("/");
|
|
23785
24155
|
}
|
|
23786
24156
|
function fromPosixRelPath(relPath) {
|
|
23787
|
-
return relPath.split("/").join(
|
|
24157
|
+
return relPath.split("/").join(path37.sep);
|
|
23788
24158
|
}
|
|
23789
24159
|
|
|
23790
24160
|
// src/transfer/export-json.ts
|
|
@@ -23800,9 +24170,9 @@ function shouldExclude(relPosix, includeTranscripts) {
|
|
|
23800
24170
|
}
|
|
23801
24171
|
async function exportJsonBundle(opts) {
|
|
23802
24172
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
23803
|
-
const outDirAbs =
|
|
23804
|
-
await
|
|
23805
|
-
const memoryDirAbs =
|
|
24173
|
+
const outDirAbs = path38.resolve(opts.outDir);
|
|
24174
|
+
await mkdir27(outDirAbs, { recursive: true });
|
|
24175
|
+
const memoryDirAbs = path38.resolve(opts.memoryDir);
|
|
23806
24176
|
const filesAbs = await listFilesRecursive(memoryDirAbs);
|
|
23807
24177
|
const records = [];
|
|
23808
24178
|
const manifestFiles = [];
|
|
@@ -23815,7 +24185,7 @@ async function exportJsonBundle(opts) {
|
|
|
23815
24185
|
manifestFiles.push({ path: relPosix, sha256, bytes });
|
|
23816
24186
|
}
|
|
23817
24187
|
if (opts.includeWorkspaceIdentity !== false && opts.workspaceDir) {
|
|
23818
|
-
const identityPath =
|
|
24188
|
+
const identityPath = path38.join(opts.workspaceDir, "IDENTITY.md");
|
|
23819
24189
|
try {
|
|
23820
24190
|
const content = await readFile26(identityPath, "utf-8");
|
|
23821
24191
|
const relPath = "workspace/IDENTITY.md";
|
|
@@ -23834,13 +24204,13 @@ async function exportJsonBundle(opts) {
|
|
|
23834
24204
|
files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
|
|
23835
24205
|
};
|
|
23836
24206
|
const bundle = { manifest, records };
|
|
23837
|
-
await writeJsonFile(
|
|
23838
|
-
await writeJsonFile(
|
|
24207
|
+
await writeJsonFile(path38.join(outDirAbs, "manifest.json"), manifest);
|
|
24208
|
+
await writeJsonFile(path38.join(outDirAbs, "bundle.json"), bundle);
|
|
23839
24209
|
}
|
|
23840
24210
|
|
|
23841
24211
|
// src/transfer/export-md.ts
|
|
23842
|
-
import
|
|
23843
|
-
import { mkdir as
|
|
24212
|
+
import path39 from "path";
|
|
24213
|
+
import { mkdir as mkdir28, readFile as readFile27, writeFile as writeFile27 } from "fs/promises";
|
|
23844
24214
|
function shouldExclude2(relPosix, includeTranscripts) {
|
|
23845
24215
|
const parts = relPosix.split("/");
|
|
23846
24216
|
if (!includeTranscripts && parts[0] === "transcripts") return true;
|
|
@@ -23848,18 +24218,18 @@ function shouldExclude2(relPosix, includeTranscripts) {
|
|
|
23848
24218
|
}
|
|
23849
24219
|
async function exportMarkdownBundle(opts) {
|
|
23850
24220
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
23851
|
-
const outDirAbs =
|
|
23852
|
-
await
|
|
23853
|
-
const memDirAbs =
|
|
24221
|
+
const outDirAbs = path39.resolve(opts.outDir);
|
|
24222
|
+
await mkdir28(outDirAbs, { recursive: true });
|
|
24223
|
+
const memDirAbs = path39.resolve(opts.memoryDir);
|
|
23854
24224
|
const filesAbs = await listFilesRecursive(memDirAbs);
|
|
23855
24225
|
const manifestFiles = [];
|
|
23856
24226
|
for (const abs of filesAbs) {
|
|
23857
24227
|
const relPosix = toPosixRelPath(abs, memDirAbs);
|
|
23858
24228
|
if (shouldExclude2(relPosix, includeTranscripts)) continue;
|
|
23859
|
-
const dstAbs =
|
|
23860
|
-
await
|
|
24229
|
+
const dstAbs = path39.join(outDirAbs, ...relPosix.split("/"));
|
|
24230
|
+
await mkdir28(path39.dirname(dstAbs), { recursive: true });
|
|
23861
24231
|
const content = await readFile27(abs);
|
|
23862
|
-
await
|
|
24232
|
+
await writeFile27(dstAbs, content);
|
|
23863
24233
|
const { sha256, bytes } = await sha256File(abs);
|
|
23864
24234
|
manifestFiles.push({ path: relPosix, sha256, bytes });
|
|
23865
24235
|
}
|
|
@@ -23871,12 +24241,12 @@ async function exportMarkdownBundle(opts) {
|
|
|
23871
24241
|
includesTranscripts: includeTranscripts,
|
|
23872
24242
|
files: manifestFiles.sort((a, b) => a.path.localeCompare(b.path))
|
|
23873
24243
|
};
|
|
23874
|
-
await writeJsonFile(
|
|
24244
|
+
await writeJsonFile(path39.join(outDirAbs, "manifest.json"), manifest);
|
|
23875
24245
|
}
|
|
23876
24246
|
async function looksLikeEngramMdExport(fromDir) {
|
|
23877
|
-
const dirAbs =
|
|
24247
|
+
const dirAbs = path39.resolve(fromDir);
|
|
23878
24248
|
try {
|
|
23879
|
-
const raw = await readFile27(
|
|
24249
|
+
const raw = await readFile27(path39.join(dirAbs, "manifest.json"), "utf-8");
|
|
23880
24250
|
const parsed = JSON.parse(raw);
|
|
23881
24251
|
return parsed.format === EXPORT_FORMAT && parsed.schemaVersion === EXPORT_SCHEMA_VERSION;
|
|
23882
24252
|
} catch {
|
|
@@ -23885,16 +24255,16 @@ async function looksLikeEngramMdExport(fromDir) {
|
|
|
23885
24255
|
}
|
|
23886
24256
|
|
|
23887
24257
|
// src/transfer/backup.ts
|
|
23888
|
-
import
|
|
23889
|
-
import { mkdir as
|
|
24258
|
+
import path40 from "path";
|
|
24259
|
+
import { mkdir as mkdir29, readdir as readdir19, rm as rm5 } from "fs/promises";
|
|
23890
24260
|
function timestampDirName(now) {
|
|
23891
24261
|
return now.toISOString().replace(/[:.]/g, "-");
|
|
23892
24262
|
}
|
|
23893
24263
|
async function backupMemoryDir(opts) {
|
|
23894
|
-
const outDirAbs =
|
|
23895
|
-
await
|
|
24264
|
+
const outDirAbs = path40.resolve(opts.outDir);
|
|
24265
|
+
await mkdir29(outDirAbs, { recursive: true });
|
|
23896
24266
|
const ts = timestampDirName(/* @__PURE__ */ new Date());
|
|
23897
|
-
const backupDir =
|
|
24267
|
+
const backupDir = path40.join(outDirAbs, ts);
|
|
23898
24268
|
await exportMarkdownBundle({
|
|
23899
24269
|
memoryDir: opts.memoryDir,
|
|
23900
24270
|
outDir: backupDir,
|
|
@@ -23919,13 +24289,13 @@ async function enforceRetention(outDirAbs, retentionDays) {
|
|
|
23919
24289
|
const tsMs = iso ? Date.parse(iso) : NaN;
|
|
23920
24290
|
if (!Number.isFinite(tsMs)) continue;
|
|
23921
24291
|
if (tsMs < cutoffMs) {
|
|
23922
|
-
await rm5(
|
|
24292
|
+
await rm5(path40.join(outDirAbs, name), { recursive: true, force: true });
|
|
23923
24293
|
}
|
|
23924
24294
|
}
|
|
23925
24295
|
}
|
|
23926
24296
|
|
|
23927
24297
|
// src/transfer/export-sqlite.ts
|
|
23928
|
-
import
|
|
24298
|
+
import path41 from "path";
|
|
23929
24299
|
import Database from "better-sqlite3";
|
|
23930
24300
|
import { readFile as readFile28 } from "fs/promises";
|
|
23931
24301
|
|
|
@@ -23953,8 +24323,8 @@ function shouldExclude3(relPosix, includeTranscripts) {
|
|
|
23953
24323
|
}
|
|
23954
24324
|
async function exportSqlite(opts) {
|
|
23955
24325
|
const includeTranscripts = opts.includeTranscripts === true;
|
|
23956
|
-
const memDirAbs =
|
|
23957
|
-
const outAbs =
|
|
24326
|
+
const memDirAbs = path41.resolve(opts.memoryDir);
|
|
24327
|
+
const outAbs = path41.resolve(opts.outFile);
|
|
23958
24328
|
const filesAbs = await listFilesRecursive(memDirAbs);
|
|
23959
24329
|
const db = new Database(outAbs);
|
|
23960
24330
|
try {
|
|
@@ -23986,8 +24356,8 @@ async function exportSqlite(opts) {
|
|
|
23986
24356
|
}
|
|
23987
24357
|
|
|
23988
24358
|
// src/transfer/import-json.ts
|
|
23989
|
-
import
|
|
23990
|
-
import { mkdir as
|
|
24359
|
+
import path42 from "path";
|
|
24360
|
+
import { mkdir as mkdir30, writeFile as writeFile28 } from "fs/promises";
|
|
23991
24361
|
|
|
23992
24362
|
// src/transfer/types.ts
|
|
23993
24363
|
import { z as z4 } from "zod";
|
|
@@ -24020,21 +24390,21 @@ function normalizeForDedupe(s) {
|
|
|
24020
24390
|
}
|
|
24021
24391
|
async function importJsonBundle(opts) {
|
|
24022
24392
|
const conflict = opts.conflict ?? "skip";
|
|
24023
|
-
const fromDirAbs =
|
|
24024
|
-
const bundlePath =
|
|
24393
|
+
const fromDirAbs = path42.resolve(opts.fromDir);
|
|
24394
|
+
const bundlePath = path42.join(fromDirAbs, "bundle.json");
|
|
24025
24395
|
const bundle = ExportBundleV1Schema.parse(await readJsonFile2(bundlePath));
|
|
24026
|
-
const memDirAbs =
|
|
24396
|
+
const memDirAbs = path42.resolve(opts.targetMemoryDir);
|
|
24027
24397
|
const written = [];
|
|
24028
24398
|
let skipped = 0;
|
|
24029
24399
|
for (const rec of bundle.records) {
|
|
24030
24400
|
const isWorkspace = rec.path.startsWith("workspace/");
|
|
24031
|
-
const targetBase = isWorkspace ? opts.workspaceDir ?
|
|
24401
|
+
const targetBase = isWorkspace ? opts.workspaceDir ? path42.resolve(opts.workspaceDir) : null : memDirAbs;
|
|
24032
24402
|
if (isWorkspace && !targetBase) {
|
|
24033
24403
|
skipped += 1;
|
|
24034
24404
|
continue;
|
|
24035
24405
|
}
|
|
24036
24406
|
const relFs = fromPosixRelPath(isWorkspace ? rec.path.replace(/^workspace\//, "") : rec.path);
|
|
24037
|
-
const absTarget =
|
|
24407
|
+
const absTarget = path42.join(targetBase, relFs);
|
|
24038
24408
|
const exists3 = await fileExists(absTarget);
|
|
24039
24409
|
if (exists3) {
|
|
24040
24410
|
if (conflict === "skip") {
|
|
@@ -24058,30 +24428,30 @@ async function importJsonBundle(opts) {
|
|
|
24058
24428
|
return { written: 0, skipped };
|
|
24059
24429
|
}
|
|
24060
24430
|
for (const w of written) {
|
|
24061
|
-
await
|
|
24062
|
-
await
|
|
24431
|
+
await mkdir30(path42.dirname(w.abs), { recursive: true });
|
|
24432
|
+
await writeFile28(w.abs, w.content, "utf-8");
|
|
24063
24433
|
}
|
|
24064
24434
|
return { written: written.length, skipped };
|
|
24065
24435
|
}
|
|
24066
24436
|
function looksLikeEngramJsonExport(fromDir) {
|
|
24067
|
-
const dir =
|
|
24437
|
+
const dir = path42.resolve(fromDir);
|
|
24068
24438
|
return Promise.all([
|
|
24069
|
-
fileExists(
|
|
24070
|
-
fileExists(
|
|
24439
|
+
fileExists(path42.join(dir, "manifest.json")),
|
|
24440
|
+
fileExists(path42.join(dir, "bundle.json"))
|
|
24071
24441
|
]).then(([m, b]) => m && b);
|
|
24072
24442
|
}
|
|
24073
24443
|
|
|
24074
24444
|
// src/transfer/import-sqlite.ts
|
|
24075
|
-
import
|
|
24445
|
+
import path43 from "path";
|
|
24076
24446
|
import Database2 from "better-sqlite3";
|
|
24077
|
-
import { mkdir as
|
|
24447
|
+
import { mkdir as mkdir31, writeFile as writeFile29 } from "fs/promises";
|
|
24078
24448
|
function normalizeForDedupe2(s) {
|
|
24079
24449
|
return s.replace(/\s+/g, " ").trim();
|
|
24080
24450
|
}
|
|
24081
24451
|
async function importSqlite(opts) {
|
|
24082
24452
|
const conflict = opts.conflict ?? "skip";
|
|
24083
|
-
const memDirAbs =
|
|
24084
|
-
const fromAbs =
|
|
24453
|
+
const memDirAbs = path43.resolve(opts.targetMemoryDir);
|
|
24454
|
+
const fromAbs = path43.resolve(opts.fromFile);
|
|
24085
24455
|
const db = new Database2(fromAbs, { readonly: true });
|
|
24086
24456
|
const written = [];
|
|
24087
24457
|
let skipped = 0;
|
|
@@ -24094,7 +24464,7 @@ async function importSqlite(opts) {
|
|
|
24094
24464
|
const rows = db.prepare("SELECT path_rel, content FROM files").all();
|
|
24095
24465
|
for (const r of rows) {
|
|
24096
24466
|
const relFs = fromPosixRelPath(r.path_rel);
|
|
24097
|
-
const absTarget =
|
|
24467
|
+
const absTarget = path43.join(memDirAbs, relFs);
|
|
24098
24468
|
const exists3 = await fileExists(absTarget);
|
|
24099
24469
|
if (exists3) {
|
|
24100
24470
|
if (conflict === "skip") {
|
|
@@ -24119,29 +24489,29 @@ async function importSqlite(opts) {
|
|
|
24119
24489
|
}
|
|
24120
24490
|
if (opts.dryRun) return { written: 0, skipped };
|
|
24121
24491
|
for (const w of written) {
|
|
24122
|
-
await
|
|
24123
|
-
await
|
|
24492
|
+
await mkdir31(path43.dirname(w.abs), { recursive: true });
|
|
24493
|
+
await writeFile29(w.abs, w.content, "utf-8");
|
|
24124
24494
|
}
|
|
24125
24495
|
return { written: written.length, skipped };
|
|
24126
24496
|
}
|
|
24127
24497
|
|
|
24128
24498
|
// src/transfer/import-md.ts
|
|
24129
|
-
import
|
|
24130
|
-
import { mkdir as
|
|
24499
|
+
import path44 from "path";
|
|
24500
|
+
import { mkdir as mkdir32, readFile as readFile29, writeFile as writeFile30 } from "fs/promises";
|
|
24131
24501
|
function normalizeForDedupe3(s) {
|
|
24132
24502
|
return s.replace(/\s+/g, " ").trim();
|
|
24133
24503
|
}
|
|
24134
24504
|
async function importMarkdownBundle(opts) {
|
|
24135
24505
|
const conflict = opts.conflict ?? "skip";
|
|
24136
|
-
const fromAbs =
|
|
24137
|
-
const targetAbs =
|
|
24506
|
+
const fromAbs = path44.resolve(opts.fromDir);
|
|
24507
|
+
const targetAbs = path44.resolve(opts.targetMemoryDir);
|
|
24138
24508
|
const filesAbs = await listFilesRecursive(fromAbs);
|
|
24139
24509
|
const writes = [];
|
|
24140
24510
|
let skipped = 0;
|
|
24141
24511
|
for (const abs of filesAbs) {
|
|
24142
24512
|
const relPosix = toPosixRelPath(abs, fromAbs);
|
|
24143
24513
|
if (relPosix === "manifest.json") continue;
|
|
24144
|
-
const dstAbs =
|
|
24514
|
+
const dstAbs = path44.join(targetAbs, fromPosixRelPath(relPosix));
|
|
24145
24515
|
const content = await readFile29(abs, "utf-8");
|
|
24146
24516
|
const exists3 = await fileExists(dstAbs);
|
|
24147
24517
|
if (exists3) {
|
|
@@ -24164,17 +24534,17 @@ async function importMarkdownBundle(opts) {
|
|
|
24164
24534
|
}
|
|
24165
24535
|
if (opts.dryRun) return { written: 0, skipped };
|
|
24166
24536
|
for (const w of writes) {
|
|
24167
|
-
await
|
|
24168
|
-
await
|
|
24537
|
+
await mkdir32(path44.dirname(w.abs), { recursive: true });
|
|
24538
|
+
await writeFile30(w.abs, w.content, "utf-8");
|
|
24169
24539
|
}
|
|
24170
24540
|
return { written: writes.length, skipped };
|
|
24171
24541
|
}
|
|
24172
24542
|
|
|
24173
24543
|
// src/transfer/autodetect.ts
|
|
24174
|
-
import
|
|
24544
|
+
import path45 from "path";
|
|
24175
24545
|
import { stat as stat9 } from "fs/promises";
|
|
24176
24546
|
async function detectImportFormat(fromPath) {
|
|
24177
|
-
const abs =
|
|
24547
|
+
const abs = path45.resolve(fromPath);
|
|
24178
24548
|
let st;
|
|
24179
24549
|
try {
|
|
24180
24550
|
st = await stat9(abs);
|
|
@@ -24659,8 +25029,8 @@ var openclawReplayNormalizer = {
|
|
|
24659
25029
|
};
|
|
24660
25030
|
|
|
24661
25031
|
// src/maintenance/archive-observations.ts
|
|
24662
|
-
import
|
|
24663
|
-
import { mkdir as
|
|
25032
|
+
import path46 from "path";
|
|
25033
|
+
import { mkdir as mkdir33, readdir as readdir20, readFile as readFile30, unlink as unlink6, writeFile as writeFile31 } from "fs/promises";
|
|
24664
25034
|
var DATE_FILE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})\.(jsonl|md)$/;
|
|
24665
25035
|
function normalizeRetentionDays(value) {
|
|
24666
25036
|
if (!Number.isFinite(value)) return 30;
|
|
@@ -24686,8 +25056,8 @@ async function listFilesRecursive2(root, relPrefix = "") {
|
|
|
24686
25056
|
return out;
|
|
24687
25057
|
}
|
|
24688
25058
|
for (const entry of entries) {
|
|
24689
|
-
const rel = relPrefix ?
|
|
24690
|
-
const full =
|
|
25059
|
+
const rel = relPrefix ? path46.join(relPrefix, entry.name) : entry.name;
|
|
25060
|
+
const full = path46.join(root, entry.name);
|
|
24691
25061
|
if (entry.isDirectory()) {
|
|
24692
25062
|
out.push(...await listFilesRecursive2(full, rel));
|
|
24693
25063
|
continue;
|
|
@@ -24697,19 +25067,19 @@ async function listFilesRecursive2(root, relPrefix = "") {
|
|
|
24697
25067
|
return out;
|
|
24698
25068
|
}
|
|
24699
25069
|
async function collectArchiveCandidates(memoryDir, cutoffTimeMs) {
|
|
24700
|
-
const roots = ["transcripts",
|
|
25070
|
+
const roots = ["transcripts", path46.join("state", "tool-usage"), path46.join("summaries", "hourly")];
|
|
24701
25071
|
const out = [];
|
|
24702
25072
|
for (const relRoot of roots) {
|
|
24703
|
-
const absRoot =
|
|
25073
|
+
const absRoot = path46.join(memoryDir, relRoot);
|
|
24704
25074
|
const files = await listFilesRecursive2(absRoot);
|
|
24705
25075
|
for (const fileRel of files) {
|
|
24706
|
-
const filename =
|
|
25076
|
+
const filename = path46.basename(fileRel);
|
|
24707
25077
|
const parsedDate = extractDateFromFilename(filename);
|
|
24708
25078
|
if (!parsedDate) continue;
|
|
24709
25079
|
if (parsedDate.getTime() >= cutoffTimeMs) continue;
|
|
24710
25080
|
out.push({
|
|
24711
|
-
absolutePath:
|
|
24712
|
-
relativePath:
|
|
25081
|
+
absolutePath: path46.join(absRoot, fileRel),
|
|
25082
|
+
relativePath: path46.join(relRoot, fileRel)
|
|
24713
25083
|
});
|
|
24714
25084
|
}
|
|
24715
25085
|
}
|
|
@@ -24724,7 +25094,7 @@ async function archiveObservations(options) {
|
|
|
24724
25094
|
new Date(now.getTime() - retentionDays * 24 * 60 * 60 * 1e3)
|
|
24725
25095
|
);
|
|
24726
25096
|
const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
24727
|
-
const archiveRoot =
|
|
25097
|
+
const archiveRoot = path46.join(options.memoryDir, "archive", "observations", stamp);
|
|
24728
25098
|
const candidates = retentionDays === 0 ? [] : await collectArchiveCandidates(
|
|
24729
25099
|
options.memoryDir,
|
|
24730
25100
|
cutoffDayStartUtc
|
|
@@ -24733,13 +25103,13 @@ async function archiveObservations(options) {
|
|
|
24733
25103
|
let archivedBytes = 0;
|
|
24734
25104
|
const archivedRelativePaths = [];
|
|
24735
25105
|
if (!dryRun && candidates.length > 0) {
|
|
24736
|
-
await
|
|
25106
|
+
await mkdir33(archiveRoot, { recursive: true });
|
|
24737
25107
|
for (const candidate of candidates) {
|
|
24738
|
-
const archivePath =
|
|
24739
|
-
const archiveDir =
|
|
24740
|
-
await
|
|
25108
|
+
const archivePath = path46.join(archiveRoot, candidate.relativePath);
|
|
25109
|
+
const archiveDir = path46.dirname(archivePath);
|
|
25110
|
+
await mkdir33(archiveDir, { recursive: true });
|
|
24741
25111
|
const raw = await readFile30(candidate.absolutePath);
|
|
24742
|
-
await
|
|
25112
|
+
await writeFile31(archivePath, raw);
|
|
24743
25113
|
await unlink6(candidate.absolutePath);
|
|
24744
25114
|
archivedFiles += 1;
|
|
24745
25115
|
archivedBytes += raw.byteLength;
|
|
@@ -24760,12 +25130,12 @@ async function archiveObservations(options) {
|
|
|
24760
25130
|
}
|
|
24761
25131
|
|
|
24762
25132
|
// src/maintenance/rebuild-observations.ts
|
|
24763
|
-
import
|
|
25133
|
+
import path48 from "path";
|
|
24764
25134
|
import { readdir as readdir21, readFile as readFile32 } from "fs/promises";
|
|
24765
25135
|
|
|
24766
25136
|
// src/maintenance/observation-ledger-utils.ts
|
|
24767
|
-
import
|
|
24768
|
-
import { mkdir as
|
|
25137
|
+
import path47 from "path";
|
|
25138
|
+
import { mkdir as mkdir34, readFile as readFile31, writeFile as writeFile32 } from "fs/promises";
|
|
24769
25139
|
function toHourBucketIso(timestamp) {
|
|
24770
25140
|
const normalized = /(?:Z|[+-]\d{2}:\d{2})$/u.test(timestamp) ? timestamp : `${timestamp}Z`;
|
|
24771
25141
|
const ms = Date.parse(normalized);
|
|
@@ -24776,8 +25146,8 @@ function toHourBucketIso(timestamp) {
|
|
|
24776
25146
|
}
|
|
24777
25147
|
async function backupAndWriteRebuiltObservations(options) {
|
|
24778
25148
|
const stamp = options.now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
24779
|
-
const archiveRoot =
|
|
24780
|
-
let backupPath =
|
|
25149
|
+
const archiveRoot = path47.join(options.memoryDir, "archive", "observations", stamp);
|
|
25150
|
+
let backupPath = path47.join(
|
|
24781
25151
|
archiveRoot,
|
|
24782
25152
|
"state",
|
|
24783
25153
|
"observation-ledger",
|
|
@@ -24785,8 +25155,8 @@ async function backupAndWriteRebuiltObservations(options) {
|
|
|
24785
25155
|
);
|
|
24786
25156
|
try {
|
|
24787
25157
|
const existing = await readFile31(options.outputPath, "utf-8");
|
|
24788
|
-
await
|
|
24789
|
-
await
|
|
25158
|
+
await mkdir34(path47.dirname(backupPath), { recursive: true });
|
|
25159
|
+
await writeFile32(backupPath, existing, "utf-8");
|
|
24790
25160
|
} catch (err) {
|
|
24791
25161
|
const code = err.code;
|
|
24792
25162
|
if (code && code === "ENOENT") {
|
|
@@ -24802,8 +25172,8 @@ async function backupAndWriteRebuiltObservations(options) {
|
|
|
24802
25172
|
rebuiltAt
|
|
24803
25173
|
})
|
|
24804
25174
|
);
|
|
24805
|
-
await
|
|
24806
|
-
await
|
|
25175
|
+
await mkdir34(path47.dirname(options.outputPath), { recursive: true });
|
|
25176
|
+
await writeFile32(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
|
|
24807
25177
|
` : "", "utf-8");
|
|
24808
25178
|
return backupPath;
|
|
24809
25179
|
}
|
|
@@ -24825,7 +25195,7 @@ async function listTranscriptFiles2(root) {
|
|
|
24825
25195
|
for (const entry of entries) {
|
|
24826
25196
|
if (entry.name === "." || entry.name === "..") continue;
|
|
24827
25197
|
if (entry.isSymbolicLink()) continue;
|
|
24828
|
-
const full =
|
|
25198
|
+
const full = path48.join(root, entry.name);
|
|
24829
25199
|
if (entry.isDirectory()) {
|
|
24830
25200
|
out.push(...await listTranscriptFiles2(full));
|
|
24831
25201
|
continue;
|
|
@@ -24885,8 +25255,8 @@ function buildLedgerRows(linesByFile) {
|
|
|
24885
25255
|
async function rebuildObservations(options) {
|
|
24886
25256
|
const dryRun = options.dryRun !== false;
|
|
24887
25257
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
24888
|
-
const transcriptsRoot =
|
|
24889
|
-
const outputPath =
|
|
25258
|
+
const transcriptsRoot = path48.join(options.memoryDir, "transcripts");
|
|
25259
|
+
const outputPath = path48.join(
|
|
24890
25260
|
options.memoryDir,
|
|
24891
25261
|
"state",
|
|
24892
25262
|
"observation-ledger",
|
|
@@ -24922,7 +25292,7 @@ async function rebuildObservations(options) {
|
|
|
24922
25292
|
}
|
|
24923
25293
|
|
|
24924
25294
|
// src/maintenance/migrate-observations.ts
|
|
24925
|
-
import
|
|
25295
|
+
import path49 from "path";
|
|
24926
25296
|
import { readdir as readdir22, readFile as readFile33 } from "fs/promises";
|
|
24927
25297
|
function toNonNegativeInt(value) {
|
|
24928
25298
|
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
@@ -24986,15 +25356,15 @@ async function listLegacyObservationFiles(root) {
|
|
|
24986
25356
|
async function migrateObservations(options) {
|
|
24987
25357
|
const dryRun = options.dryRun !== false;
|
|
24988
25358
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
24989
|
-
const ledgerRoot =
|
|
24990
|
-
const outputPath =
|
|
25359
|
+
const ledgerRoot = path49.join(options.memoryDir, "state", "observation-ledger");
|
|
25360
|
+
const outputPath = path49.join(ledgerRoot, "rebuilt-observations.jsonl");
|
|
24991
25361
|
const legacyFiles = await listLegacyObservationFiles(ledgerRoot);
|
|
24992
|
-
const sourceRelativePaths = legacyFiles.map((name) =>
|
|
25362
|
+
const sourceRelativePaths = legacyFiles.map((name) => path49.join("state", "observation-ledger", name));
|
|
24993
25363
|
const byKey = /* @__PURE__ */ new Map();
|
|
24994
25364
|
let parsedRows = 0;
|
|
24995
25365
|
let malformedLines = 0;
|
|
24996
25366
|
for (const file of legacyFiles) {
|
|
24997
|
-
const full =
|
|
25367
|
+
const full = path49.join(ledgerRoot, file);
|
|
24998
25368
|
const raw = await readFile33(full, "utf-8");
|
|
24999
25369
|
for (const line of raw.split("\n")) {
|
|
25000
25370
|
if (!line.trim()) continue;
|
|
@@ -25184,10 +25554,10 @@ var defaultCommandRunner = (command, args, options) => {
|
|
|
25184
25554
|
|
|
25185
25555
|
// src/network/webdav.ts
|
|
25186
25556
|
import { createReadStream } from "fs";
|
|
25187
|
-
import { mkdir as
|
|
25557
|
+
import { mkdir as mkdir35, readdir as readdir23, realpath as realpath2, stat as stat11 } from "fs/promises";
|
|
25188
25558
|
import { createServer } from "http";
|
|
25189
25559
|
import { timingSafeEqual } from "crypto";
|
|
25190
|
-
import
|
|
25560
|
+
import path50 from "path";
|
|
25191
25561
|
import { pipeline } from "stream/promises";
|
|
25192
25562
|
import { URL as URL2 } from "url";
|
|
25193
25563
|
function hostToUrlAuthority(host) {
|
|
@@ -25223,10 +25593,10 @@ var WebDavServer = class _WebDavServer {
|
|
|
25223
25593
|
const allowedRoots = [];
|
|
25224
25594
|
const aliasSet = /* @__PURE__ */ new Set();
|
|
25225
25595
|
for (const dir of options.allowlistDirs) {
|
|
25226
|
-
const resolved =
|
|
25227
|
-
await
|
|
25596
|
+
const resolved = path50.resolve(dir);
|
|
25597
|
+
await mkdir35(resolved, { recursive: true });
|
|
25228
25598
|
const canonical = await realpath2(resolved);
|
|
25229
|
-
const alias =
|
|
25599
|
+
const alias = path50.basename(canonical) || "root";
|
|
25230
25600
|
if (aliasSet.has(alias)) {
|
|
25231
25601
|
throw new Error(`duplicate webdav allowlist alias: ${alias}`);
|
|
25232
25602
|
}
|
|
@@ -25382,7 +25752,7 @@ var WebDavServer = class _WebDavServer {
|
|
|
25382
25752
|
if (decodedPath.includes("\0")) {
|
|
25383
25753
|
return { ok: false, code: 400, message: "invalid path" };
|
|
25384
25754
|
}
|
|
25385
|
-
const normalized =
|
|
25755
|
+
const normalized = path50.posix.normalize(decodedPath);
|
|
25386
25756
|
const segments = normalized.split("/").filter((segment) => segment.length > 0);
|
|
25387
25757
|
if (segments.length === 0) {
|
|
25388
25758
|
return { ok: false, code: 403, message: "root listing is not allowed" };
|
|
@@ -25396,7 +25766,7 @@ var WebDavServer = class _WebDavServer {
|
|
|
25396
25766
|
if (relative.some((segment) => segment === ".." || segment.includes("\\"))) {
|
|
25397
25767
|
return { ok: false, code: 403, message: "path traversal is not allowed" };
|
|
25398
25768
|
}
|
|
25399
|
-
const candidate =
|
|
25769
|
+
const candidate = path50.resolve(root.absolute, ...relative);
|
|
25400
25770
|
if (!this.isPathInside(root.absolute, candidate)) {
|
|
25401
25771
|
return { ok: false, code: 403, message: "path escaped allowlist" };
|
|
25402
25772
|
}
|
|
@@ -25474,10 +25844,10 @@ var WebDavServer = class _WebDavServer {
|
|
|
25474
25844
|
}
|
|
25475
25845
|
isPathInside(root, target) {
|
|
25476
25846
|
if (target === root) return true;
|
|
25477
|
-
if (root ===
|
|
25847
|
+
if (root === path50.parse(root).root) {
|
|
25478
25848
|
return target.startsWith(root);
|
|
25479
25849
|
}
|
|
25480
|
-
return target.startsWith(`${root}${
|
|
25850
|
+
return target.startsWith(`${root}${path50.sep}`);
|
|
25481
25851
|
}
|
|
25482
25852
|
};
|
|
25483
25853
|
function xmlEscape(value) {
|
|
@@ -25492,10 +25862,10 @@ import { createHash as createHash9 } from "crypto";
|
|
|
25492
25862
|
import { createServer as createServer2 } from "http";
|
|
25493
25863
|
import { watch } from "fs";
|
|
25494
25864
|
import { readFile as readFile35 } from "fs/promises";
|
|
25495
|
-
import
|
|
25865
|
+
import path52 from "path";
|
|
25496
25866
|
|
|
25497
25867
|
// src/graph-dashboard-parser.ts
|
|
25498
|
-
import
|
|
25868
|
+
import path51 from "path";
|
|
25499
25869
|
import { readFile as readFile34 } from "fs/promises";
|
|
25500
25870
|
|
|
25501
25871
|
// src/graph-dashboard-key.ts
|
|
@@ -25506,7 +25876,7 @@ function graphEdgeKey(edge) {
|
|
|
25506
25876
|
// src/graph-dashboard-parser.ts
|
|
25507
25877
|
var GRAPH_TYPES = ["entity", "time", "causal"];
|
|
25508
25878
|
function graphFile(memoryDir, type) {
|
|
25509
|
-
return
|
|
25879
|
+
return path51.join(memoryDir, "state", "graphs", `${type}.jsonl`);
|
|
25510
25880
|
}
|
|
25511
25881
|
function isGraphEdge(raw, expectedType) {
|
|
25512
25882
|
if (!raw || typeof raw !== "object") return false;
|
|
@@ -25633,7 +26003,7 @@ var GraphDashboardServer = class {
|
|
|
25633
26003
|
this.memoryDir = options.memoryDir;
|
|
25634
26004
|
this.host = options.host?.trim() || "127.0.0.1";
|
|
25635
26005
|
this.requestedPort = Number.isFinite(options.port) ? Math.max(0, Math.floor(options.port ?? 0)) : 0;
|
|
25636
|
-
this.publicDir = options.publicDir ??
|
|
26006
|
+
this.publicDir = options.publicDir ?? path52.join(process.cwd(), "dashboard", "public");
|
|
25637
26007
|
this.watchDebounceMs = Math.max(50, Math.floor(options.watchDebounceMs ?? 300));
|
|
25638
26008
|
}
|
|
25639
26009
|
async start() {
|
|
@@ -25732,11 +26102,11 @@ var GraphDashboardServer = class {
|
|
|
25732
26102
|
return;
|
|
25733
26103
|
}
|
|
25734
26104
|
if (req.method === "GET" && url === "/app.js") {
|
|
25735
|
-
await this.respondStatic(res,
|
|
26105
|
+
await this.respondStatic(res, path52.join(this.publicDir, "app.js"), "application/javascript; charset=utf-8");
|
|
25736
26106
|
return;
|
|
25737
26107
|
}
|
|
25738
26108
|
if (req.method === "GET" && (url === "/" || url === "/index.html")) {
|
|
25739
|
-
await this.respondStatic(res,
|
|
26109
|
+
await this.respondStatic(res, path52.join(this.publicDir, "index.html"), "text/html; charset=utf-8");
|
|
25740
26110
|
return;
|
|
25741
26111
|
}
|
|
25742
26112
|
this.respondJson(res, 404, { error: "Not found" });
|
|
@@ -25822,7 +26192,7 @@ var GraphDashboardServer = class {
|
|
|
25822
26192
|
}
|
|
25823
26193
|
}
|
|
25824
26194
|
startWatcher() {
|
|
25825
|
-
const graphDir =
|
|
26195
|
+
const graphDir = path52.join(this.memoryDir, "state", "graphs");
|
|
25826
26196
|
try {
|
|
25827
26197
|
this.watcher = watch(graphDir, { persistent: false }, () => {
|
|
25828
26198
|
this.scheduleRebuild();
|
|
@@ -25868,7 +26238,7 @@ var GraphDashboardServer = class {
|
|
|
25868
26238
|
|
|
25869
26239
|
// src/compat/checks.ts
|
|
25870
26240
|
import { access as access2, readFile as readFile36 } from "fs/promises";
|
|
25871
|
-
import
|
|
26241
|
+
import path53 from "path";
|
|
25872
26242
|
import { spawn as spawn4 } from "child_process";
|
|
25873
26243
|
var REQUIRED_HOOKS = ["before_agent_start", "agent_end"];
|
|
25874
26244
|
function isSafeCommandToken(command) {
|
|
@@ -26050,9 +26420,9 @@ function compareVersions(a, b) {
|
|
|
26050
26420
|
async function runCompatChecks(options) {
|
|
26051
26421
|
const checks = [];
|
|
26052
26422
|
const runner = options.runner ?? defaultRunner;
|
|
26053
|
-
const pluginJsonPath =
|
|
26054
|
-
const packageJsonPath =
|
|
26055
|
-
const indexPath =
|
|
26423
|
+
const pluginJsonPath = path53.join(options.repoRoot, "openclaw.plugin.json");
|
|
26424
|
+
const packageJsonPath = path53.join(options.repoRoot, "package.json");
|
|
26425
|
+
const indexPath = path53.join(options.repoRoot, "src", "index.ts");
|
|
26056
26426
|
let pluginRaw = "";
|
|
26057
26427
|
let pluginManifestPresent = false;
|
|
26058
26428
|
try {
|
|
@@ -26237,111 +26607,6 @@ async function runCompatChecks(options) {
|
|
|
26237
26607
|
};
|
|
26238
26608
|
}
|
|
26239
26609
|
|
|
26240
|
-
// src/trust-zones.ts
|
|
26241
|
-
import path53 from "path";
|
|
26242
|
-
import { mkdir as mkdir35, writeFile as writeFile32 } from "fs/promises";
|
|
26243
|
-
function validateMetadata3(raw) {
|
|
26244
|
-
return validateStringRecord(raw, "metadata");
|
|
26245
|
-
}
|
|
26246
|
-
function validateZone(raw, field) {
|
|
26247
|
-
const value = assertString2(raw, field);
|
|
26248
|
-
if (!["quarantine", "working", "trusted"].includes(value)) {
|
|
26249
|
-
throw new Error(`${field} must be one of quarantine|working|trusted`);
|
|
26250
|
-
}
|
|
26251
|
-
return value;
|
|
26252
|
-
}
|
|
26253
|
-
function validateKind(raw) {
|
|
26254
|
-
const value = assertString2(raw, "kind");
|
|
26255
|
-
if (!["memory", "artifact", "state", "trajectory", "external"].includes(value)) {
|
|
26256
|
-
throw new Error("kind must be one of memory|artifact|state|trajectory|external");
|
|
26257
|
-
}
|
|
26258
|
-
return value;
|
|
26259
|
-
}
|
|
26260
|
-
function validateProvenance(raw) {
|
|
26261
|
-
if (!isRecord2(raw)) throw new Error("provenance must be an object");
|
|
26262
|
-
const sourceClass = assertString2(raw.sourceClass, "provenance.sourceClass");
|
|
26263
|
-
if (!["tool_output", "web_content", "subagent_trace", "system_memory", "user_input", "manual"].includes(sourceClass)) {
|
|
26264
|
-
throw new Error("provenance.sourceClass must be one of tool_output|web_content|subagent_trace|system_memory|user_input|manual");
|
|
26265
|
-
}
|
|
26266
|
-
return {
|
|
26267
|
-
sourceClass,
|
|
26268
|
-
observedAt: assertIsoRecordedAt(assertString2(raw.observedAt, "provenance.observedAt"), "provenance.observedAt"),
|
|
26269
|
-
sessionKey: optionalString(raw.sessionKey),
|
|
26270
|
-
sourceId: optionalString(raw.sourceId),
|
|
26271
|
-
evidenceHash: optionalString(raw.evidenceHash)
|
|
26272
|
-
};
|
|
26273
|
-
}
|
|
26274
|
-
function resolveTrustZoneStoreDir(memoryDir, overrideDir) {
|
|
26275
|
-
if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
|
|
26276
|
-
return overrideDir.trim();
|
|
26277
|
-
}
|
|
26278
|
-
return path53.join(memoryDir, "state", "trust-zones");
|
|
26279
|
-
}
|
|
26280
|
-
function validateTrustZoneRecord(raw) {
|
|
26281
|
-
if (!isRecord2(raw)) throw new Error("trust-zone record must be an object");
|
|
26282
|
-
if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
|
|
26283
|
-
return {
|
|
26284
|
-
schemaVersion: 1,
|
|
26285
|
-
recordId: assertSafePathSegment(assertString2(raw.recordId, "recordId"), "recordId"),
|
|
26286
|
-
zone: validateZone(raw.zone, "zone"),
|
|
26287
|
-
recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
|
|
26288
|
-
kind: validateKind(raw.kind),
|
|
26289
|
-
summary: assertString2(raw.summary, "summary"),
|
|
26290
|
-
provenance: validateProvenance(raw.provenance),
|
|
26291
|
-
promotedFromZone: raw.promotedFromZone === void 0 ? void 0 : validateZone(raw.promotedFromZone, "promotedFromZone"),
|
|
26292
|
-
entityRefs: optionalStringArray2(raw.entityRefs, "entityRefs"),
|
|
26293
|
-
tags: optionalStringArray2(raw.tags, "tags"),
|
|
26294
|
-
metadata: validateMetadata3(raw.metadata)
|
|
26295
|
-
};
|
|
26296
|
-
}
|
|
26297
|
-
async function readTrustZoneRecords(options) {
|
|
26298
|
-
const rootDir = resolveTrustZoneStoreDir(options.memoryDir, options.trustZoneStoreDir);
|
|
26299
|
-
const files = await listJsonFiles(path53.join(rootDir, "zones"));
|
|
26300
|
-
const records = [];
|
|
26301
|
-
const invalidRecords = [];
|
|
26302
|
-
for (const filePath of files) {
|
|
26303
|
-
try {
|
|
26304
|
-
records.push(validateTrustZoneRecord(await readJsonFile(filePath)));
|
|
26305
|
-
} catch (error) {
|
|
26306
|
-
invalidRecords.push({
|
|
26307
|
-
path: filePath,
|
|
26308
|
-
error: error instanceof Error ? error.message : String(error)
|
|
26309
|
-
});
|
|
26310
|
-
}
|
|
26311
|
-
}
|
|
26312
|
-
return { files, records, invalidRecords };
|
|
26313
|
-
}
|
|
26314
|
-
async function getTrustZoneStoreStatus(options) {
|
|
26315
|
-
const rootDir = resolveTrustZoneStoreDir(options.memoryDir, options.trustZoneStoreDir);
|
|
26316
|
-
const zonesDir = path53.join(rootDir, "zones");
|
|
26317
|
-
const { files, records, invalidRecords } = await readTrustZoneRecords(options);
|
|
26318
|
-
records.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
|
|
26319
|
-
const byZone = {};
|
|
26320
|
-
const byKind = {};
|
|
26321
|
-
for (const record of records) {
|
|
26322
|
-
byZone[record.zone] = (byZone[record.zone] ?? 0) + 1;
|
|
26323
|
-
byKind[record.kind] = (byKind[record.kind] ?? 0) + 1;
|
|
26324
|
-
}
|
|
26325
|
-
return {
|
|
26326
|
-
enabled: options.enabled,
|
|
26327
|
-
promotionEnabled: options.promotionEnabled,
|
|
26328
|
-
rootDir,
|
|
26329
|
-
zonesDir,
|
|
26330
|
-
records: {
|
|
26331
|
-
total: files.length,
|
|
26332
|
-
valid: records.length,
|
|
26333
|
-
invalid: invalidRecords.length,
|
|
26334
|
-
byZone,
|
|
26335
|
-
byKind,
|
|
26336
|
-
latestRecordId: records[0]?.recordId,
|
|
26337
|
-
latestRecordedAt: records[0]?.recordedAt,
|
|
26338
|
-
latestZone: records[0]?.zone
|
|
26339
|
-
},
|
|
26340
|
-
latestRecord: records[0],
|
|
26341
|
-
invalidRecords
|
|
26342
|
-
};
|
|
26343
|
-
}
|
|
26344
|
-
|
|
26345
26610
|
// src/cli.ts
|
|
26346
26611
|
function rankCandidateForKeep(a, b) {
|
|
26347
26612
|
const aConfidence = typeof a.frontmatter.confidence === "number" ? a.frontmatter.confidence : 0;
|
|
@@ -26546,6 +26811,24 @@ async function runTrustZoneStatusCliCommand(options) {
|
|
|
26546
26811
|
promotionEnabled: options.quarantinePromotionEnabled
|
|
26547
26812
|
});
|
|
26548
26813
|
}
|
|
26814
|
+
async function runTrustZonePromoteCliCommand(options) {
|
|
26815
|
+
const result = await promoteTrustZoneRecord({
|
|
26816
|
+
memoryDir: options.memoryDir,
|
|
26817
|
+
trustZoneStoreDir: options.trustZoneStoreDir,
|
|
26818
|
+
enabled: options.trustZonesEnabled,
|
|
26819
|
+
promotionEnabled: options.quarantinePromotionEnabled,
|
|
26820
|
+
sourceRecordId: options.sourceRecordId,
|
|
26821
|
+
targetZone: options.targetZone,
|
|
26822
|
+
recordedAt: options.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
26823
|
+
promotionReason: options.promotionReason,
|
|
26824
|
+
summary: options.summary,
|
|
26825
|
+
dryRun: options.dryRun === true
|
|
26826
|
+
});
|
|
26827
|
+
return {
|
|
26828
|
+
...result,
|
|
26829
|
+
dryRun: options.dryRun === true
|
|
26830
|
+
};
|
|
26831
|
+
}
|
|
26549
26832
|
async function runSessionCheckCliCommand(options) {
|
|
26550
26833
|
return analyzeSessionIntegrity({ memoryDir: options.memoryDir });
|
|
26551
26834
|
}
|
|
@@ -27714,6 +27997,23 @@ function registerCli(api, orchestrator) {
|
|
|
27714
27997
|
console.log(JSON.stringify(status, null, 2));
|
|
27715
27998
|
console.log("OK");
|
|
27716
27999
|
});
|
|
28000
|
+
cmd.command("trust-zone-promote").description("Dry-run or apply a trust-zone promotion with provenance enforcement").requiredOption("--record-id <recordId>", "Source trust-zone record id").requiredOption("--target-zone <targetZone>", "Promotion target zone (working|trusted)").requiredOption("--reason <reason>", "Human-readable promotion reason").option("--recorded-at <isoTimestamp>", "Promotion timestamp (defaults to now)").option("--summary <summary>", "Optional replacement summary for the promoted record").option("--dry-run", "Show the promotion plan without writing the promoted record").action(async (...args) => {
|
|
28001
|
+
const options = args[0] ?? {};
|
|
28002
|
+
const result = await runTrustZonePromoteCliCommand({
|
|
28003
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
28004
|
+
trustZoneStoreDir: orchestrator.config.trustZoneStoreDir,
|
|
28005
|
+
trustZonesEnabled: orchestrator.config.trustZonesEnabled,
|
|
28006
|
+
quarantinePromotionEnabled: orchestrator.config.quarantinePromotionEnabled,
|
|
28007
|
+
sourceRecordId: String(options.recordId ?? ""),
|
|
28008
|
+
targetZone: String(options.targetZone ?? ""),
|
|
28009
|
+
promotionReason: String(options.reason ?? ""),
|
|
28010
|
+
recordedAt: typeof options.recordedAt === "string" ? options.recordedAt : void 0,
|
|
28011
|
+
summary: typeof options.summary === "string" ? options.summary : void 0,
|
|
28012
|
+
dryRun: options.dryRun === true
|
|
28013
|
+
});
|
|
28014
|
+
console.log(JSON.stringify(result, null, 2));
|
|
28015
|
+
console.log("OK");
|
|
28016
|
+
});
|
|
27717
28017
|
cmd.command("conversation-index-health").description("Show conversation index backend health and index stats").action(async () => {
|
|
27718
28018
|
const health = await runConversationIndexHealthCliCommand(orchestrator);
|
|
27719
28019
|
console.log(JSON.stringify(health, null, 2));
|