@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/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 path33 from "path";
626
+ import path34 from "path";
620
627
  import os5 from "os";
621
628
  import { createHash as createHash6 } from "crypto";
622
- import { mkdir as mkdir23, readdir as readdir16, readFile as readFile23, stat as stat7, unlink as unlink5, writeFile as writeFile23 } from "fs/promises";
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 = countOverlap(queryTokens, value);
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(normalizeTokens(options.query));
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 normalizeTokens2(value) {
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(normalizeTokens2(value));
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(normalizeTokens2(options.query));
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 mkdir17, writeFile as writeFile18 } from "fs/promises";
14684
- import path24 from "path";
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 = path24.join(rootDir, safe, date);
14695
- await mkdir17(dir, { recursive: true });
14696
- const fp = path24.join(dir, `${c.id}.md`);
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 writeFile18(fp, content, "utf-8");
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 path25 from "path";
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 = path25.join(rootDir, s.name);
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(path25.join(sessionDir, d.name), { recursive: true, force: true });
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 path26 from "path";
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 = path26.dirname(currentFile);
14771
- if (moduleDir.endsWith(`${path26.sep}conversation-index`)) {
14772
- return path26.resolve(moduleDir, "..", "..", "scripts", "faiss_index.py");
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 path26.resolve(moduleDir, "..", "scripts", "faiss_index.py");
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 = path26.isAbsolute(config.indexDir) ? config.indexDir : path26.join(config.memoryDir, config.indexDir);
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 path27 from "path";
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 = path27.join(this.config.memoryDir, "namespaces", this.config.defaultNamespace);
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 path27.join(this.config.memoryDir, "namespaces", namespace);
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 mkdir18, readFile as readFile19, readdir as readdir14, appendFile as appendFile4, writeFile as writeFile19, stat as stat5 } from "fs/promises";
15070
- import path28 from "path";
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 : path28.join(os3.homedir(), ".openclaw", "workspace", "shared-context");
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 = path28.join(base, "priorities.md");
15269
- this.prioritiesInboxPath = path28.join(base, "priorities.inbox.md");
15270
- this.outputsDir = path28.join(base, "agent-outputs");
15271
- this.roundtableDir = path28.join(base, "roundtable");
15272
- this.feedbackDir = path28.join(base, "feedback");
15273
- this.feedbackInboxPath = path28.join(this.feedbackDir, "inbox.jsonl");
15274
- this.crossSignalsDir = path28.join(base, "cross-signals");
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 mkdir18(this.dir, { recursive: true });
15286
- await mkdir18(this.outputsDir, { recursive: true });
15287
- await mkdir18(this.roundtableDir, { recursive: true });
15288
- await mkdir18(this.feedbackDir, { recursive: true });
15289
- await mkdir18(this.crossSignalsDir, { recursive: true });
15290
- await mkdir18(path28.join(this.dir, "staging"), { recursive: true });
15291
- await mkdir18(path28.join(this.dir, "kpis"), { recursive: true });
15292
- await mkdir18(path28.join(this.dir, "calendar"), { recursive: true });
15293
- await mkdir18(path28.join(this.dir, "content-calendar"), { recursive: true });
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 writeFile19(fp, content, "utf-8");
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] ? path28.join(this.roundtableDir, files[0]) : null;
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 = path28.join(this.outputsDir, opts.agentId, date);
15350
- await mkdir18(dir, { recursive: true });
15351
- const fp = path28.join(dir, `${time}-${slug}.md`);
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 writeFile19(fp, body, "utf-8");
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 = path28.join(this.outputsDir, a.name, date);
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 = path28.join(dayDir, f);
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 = path28.join(this.crossSignalsDir, `${date}.json`);
15498
- await writeFile19(crossSignalsPath, `${JSON.stringify(crossSignalReport, null, 2)}
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 = path28.join(this.roundtableDir, `${date}.md`);
15522
- await writeFile19(roundtablePath, trimmed, "utf-8");
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 mkdir19, readFile as readFile20, readdir as readdir15, writeFile as writeFile20 } from "fs/promises";
15535
- import path29 from "path";
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 path29.join(os4.homedir(), ".openclaw", "workspace", "shared-context");
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 = path29.join(config.memoryDir, "compounding", "weekly");
15602
- this.rubricsPath = path29.join(config.memoryDir, "compounding", "rubrics.md");
15603
- this.mistakesPath = path29.join(config.memoryDir, "compounding", "mistakes.json");
15604
- this.feedbackInboxPath = path29.join(sharedContextDir(config), "feedback", "inbox.jsonl");
15605
- this.identityAnchorPath = path29.join(config.memoryDir, "identity", "identity-anchor.md");
15606
- this.identityIncidentsDir = path29.join(config.memoryDir, "identity", "incidents");
15607
- this.identityAuditWeeklyDir = path29.join(config.memoryDir, "identity", "audits", "weekly");
15608
- this.identityAuditMonthlyDir = path29.join(config.memoryDir, "identity", "audits", "monthly");
15609
- this.identityImprovementLoopsPath = path29.join(config.memoryDir, "identity", "improvement-loops.md");
15610
- this.memoryActionEventsPath = path29.join(config.memoryDir, "state", "memory-actions.jsonl");
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 mkdir19(this.weeklyDir, { recursive: true });
15624
- await mkdir19(path29.dirname(this.mistakesPath), { recursive: true });
15625
- await mkdir19(path29.dirname(this.rubricsPath), { recursive: true });
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 = path29.join(this.weeklyDir, `${weekId}.md`);
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 writeFile20(reportPath, md, "utf-8");
15955
+ await writeFile21(reportPath, md, "utf-8");
15639
15956
  const rubrics = this.formatRubrics(entries, outcomeSummary);
15640
- await writeFile20(this.rubricsPath, rubrics, "utf-8");
15641
- await writeFile20(
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 mkdir19(dir, { recursive: true });
15722
- const reportPath = path29.join(dir, `${key}.md`);
15723
- await writeFile20(reportPath, lines.join("\n"), "utf-8");
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(`${path29.basename(this.memoryActionEventsPath)}:L${event.line}`);
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 = [`${path29.basename(wrapped.sourcePath)}:L${wrapped.sourceLine}#${wrapped.entryId}`];
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: [`${path29.basename(this.memoryActionEventsPath)}:*`] });
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) => `${path29.basename(e.sourcePath)}:L${e.sourceLine}#${e.entryId}`);
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: ${path29.basename(item.sourcePath)}:L${item.sourceLine}#${item.entryId})_`);
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 = path29.join(this.identityIncidentsDir, file);
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 = path29.join(this.identityAuditWeeklyDir, `${weekId}.md`);
16063
- const monthlyPath = path29.join(this.identityAuditMonthlyDir, `${monthId}.md`);
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 mkdir20 } from "fs/promises";
16077
- import path30 from "path";
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 ?? path30.join(this.storage.dir, "state", "tier-migration-journal.jsonl");
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 mkdir20(path30.dirname(this.journalPath), { recursive: true });
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 mkdir21, readFile as readFile21, realpath, rename as rename2, rm as rm3, stat as stat6, writeFile as writeFile21 } from "fs/promises";
16312
- import path31 from "path";
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 = path31.resolve(memoryDir);
16332
- const defaultPath = path31.join(root, "state", "routing-rules.json");
16333
- if (path31.isAbsolute(stateFile)) {
16334
- const absolute = path31.resolve(stateFile);
16335
- return absolute.startsWith(root + path31.sep) ? absolute : defaultPath;
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 = path31.resolve(root, stateFile);
16338
- return resolved.startsWith(root + path31.sep) ? resolved : defaultPath;
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 = path31.resolve(memoryDir);
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 writeFile21(this.statePath, JSON.stringify(payload, null, 2), "utf-8");
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 writeFile21(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
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 mkdir21(path31.dirname(this.lockPath), { recursive: true });
16794
+ await mkdir22(path32.dirname(this.lockPath), { recursive: true });
16478
16795
  while (Date.now() - start < timeoutMs) {
16479
16796
  try {
16480
- await mkdir21(this.lockPath);
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 mkdir21(this.memoryRoot, { recursive: true });
16827
+ await mkdir22(this.memoryRoot, { recursive: true });
16511
16828
  const canonicalRoot = await realpath(this.memoryRoot);
16512
- const canonicalParent = await this.canonicalizePathWithoutCreating(path31.dirname(this.statePath));
16513
- const canonicalStatePath = path31.join(canonicalParent, path31.basename(this.statePath));
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 mkdir21(path31.dirname(this.statePath), { recursive: true });
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 = path31.resolve(root);
16535
- const normalizedCandidate = path31.resolve(candidate);
16851
+ const normalizedRoot = path32.resolve(root);
16852
+ const normalizedCandidate = path32.resolve(candidate);
16536
16853
  if (normalizedCandidate === normalizedRoot) return true;
16537
- if (normalizedRoot === path31.parse(normalizedRoot).root) {
16854
+ if (normalizedRoot === path32.parse(normalizedRoot).root) {
16538
16855
  return normalizedCandidate.startsWith(normalizedRoot);
16539
16856
  }
16540
- return normalizedCandidate.startsWith(`${normalizedRoot}${path31.sep}`);
16857
+ return normalizedCandidate.startsWith(`${normalizedRoot}${path32.sep}`);
16541
16858
  }
16542
16859
  async canonicalizePathWithoutCreating(targetPath) {
16543
- const absoluteTarget = path31.resolve(targetPath);
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 = path31.relative(probe, absoluteTarget);
16549
- return path31.resolve(canonicalProbe, remainder);
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 = path31.dirname(probe);
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 path32 from "path";
16567
- import { mkdir as mkdir22, readFile as readFile22, rename as rename3, writeFile as writeFile22 } from "fs/promises";
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 mkdir22(path32.dirname(filePath), { recursive: true });
16616
- await writeFile22(tempPath, `${JSON.stringify(snapshot, null, 2)}
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 = path32.join(memoryDir, "state");
16625
- this.runtimePath = path32.join(stateDir2, RUNTIME_POLICY_FILE);
16626
- this.runtimePrevPath = path32.join(stateDir2, RUNTIME_POLICY_PREV_FILE);
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 path33.join(os5.homedir(), ".openclaw", "workspace");
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 = path33.isAbsolute(candidatePath) ? candidatePath : path33.resolve(storageDir, candidatePath);
16920
- const rel = path33.relative(storageDir, absolutePath);
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(path33.sep).join("/");
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, path33.relative(storageDir, mem.path));
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: path33.join(options.storageDir, options.memoryRelPath),
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 path33.join("corrections", `${options.memoryId}.md`);
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 path33.join("facts", day, `${options.memoryId}.md`);
17349
+ return path34.join("facts", day, `${options.memoryId}.md`);
17033
17350
  }
17034
- return path33.join("facts", `${options.memoryId}.md`);
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 = path33.join(config.memoryDir, "conversation-index", "chunks");
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
- path33.join(config.memoryDir, "threads"),
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 = path33.join(this.config.memoryDir, "state");
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
- path33.join(this.config.memoryDir, "conversation-index")
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 = path33.join(wsDir, f);
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 = path33.isAbsolute(rel) ? rel : path33.join(this.config.workspaceDir, rel);
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 = path33.join(this.config.workspaceDir, hygiene.archiveDir);
17452
- const base = path33.basename(abs);
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 writeFile23(abs, newContent, "utf-8");
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 = path33.join(this.config.memoryDir, hygiene.warningsLogPath);
17478
- await mkdir23(path33.dirname(fp), { recursive: true });
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 writeFile23(fp, existing + block, "utf-8");
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 = path33.join(storage.dir, "state", "last_graph_recall.json");
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 = path33.join(dir, entry.name);
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) => path33.join(storage.dir, 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 = path33.resolve(storage.dir, candidate.path);
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: path33.resolve(storage.dir, candidate.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 = path33.join(options.storage.dir, "state", "last_graph_recall.json");
18011
- await mkdir23(path33.dirname(snapshotPath), { recursive: true });
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 writeFile23(snapshotPath, JSON.stringify(payload, null, 2), "utf-8");
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 = path33.join(workspaceDir, `.compaction-reset-signal-${safeSessionKey}`);
18390
- const bootPath = path33.join(workspaceDir, "BOOT.md");
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(path33.join(storage.dir, "cold"));
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 = path33.relative(storage.dir, m.path);
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 = path33.join(this.storage.dir, "state", "lifecycle-metrics.json");
20547
- await mkdir23(path33.dirname(metricsPath), { recursive: true });
20548
- await writeFile23(metricsPath, JSON.stringify(metrics, null, 2), "utf-8");
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 = path33.isAbsolute(hit.path) ? hit.path : path33.join(this.config.memoryDir, hit.path);
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 = path33.resolve(storageDir);
21335
- const resolvedMemoryDir = path33.resolve(this.config.memoryDir);
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 path35 from "path";
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 path34 from "path";
21738
+ import path35 from "path";
21369
21739
  import { randomUUID } from "crypto";
21370
- import { mkdir as mkdir24, readdir as readdir17, readFile as readFile24, rm as rm4, writeFile as writeFile24 } from "fs/promises";
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 = path34.join(memoryDir, "work", "tasks");
21446
- this.projectsDir = path34.join(memoryDir, "work", "projects");
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 mkdir24(this.tasksDir, { recursive: true });
21452
- await mkdir24(this.projectsDir, { recursive: true });
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 path34.join(this.tasksDir, `${id}.md`);
21826
+ return path35.join(this.tasksDir, `${id}.md`);
21457
21827
  }
21458
21828
  projectPath(id) {
21459
21829
  assertValidWorkId(id, "project");
21460
- return path34.join(this.projectsDir, `${id}.md`);
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 writeFile24(this.taskPath(task.id), this.serializeTask(task), "utf-8");
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(path34.join(this.tasksDir, entry.name), "utf-8");
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 writeFile24(this.taskPath(id), this.serializeTask(next), "utf-8");
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 writeFile24(this.projectPath(project.id), this.serializeProject(project), "utf-8");
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(path34.join(this.projectsDir, entry.name), "utf-8");
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 writeFile24(this.projectPath(id), this.serializeProject(next), "utf-8");
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 = path35.join(process.env.HOME ?? "~", ".openclaw", "workspace");
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 path37 from "path";
23730
- import { mkdir as mkdir26, readFile as readFile26 } from "fs/promises";
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 mkdir25, readdir as readdir18, readFile as readFile25, stat as stat8, writeFile as writeFile25 } from "fs/promises";
23739
- import path36 from "path";
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 mkdir25(path36.dirname(filePath), { recursive: true });
23752
- await writeFile25(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
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 = path36.join(dir, ent.name);
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 = path36.relative(rootDir, absPath);
23784
- return rel.split(path36.sep).join("/");
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(path36.sep);
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 = path37.resolve(opts.outDir);
23804
- await mkdir26(outDirAbs, { recursive: true });
23805
- const memoryDirAbs = path37.resolve(opts.memoryDir);
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 = path37.join(opts.workspaceDir, "IDENTITY.md");
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(path37.join(outDirAbs, "manifest.json"), manifest);
23838
- await writeJsonFile(path37.join(outDirAbs, "bundle.json"), bundle);
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 path38 from "path";
23843
- import { mkdir as mkdir27, readFile as readFile27, writeFile as writeFile26 } from "fs/promises";
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 = path38.resolve(opts.outDir);
23852
- await mkdir27(outDirAbs, { recursive: true });
23853
- const memDirAbs = path38.resolve(opts.memoryDir);
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 = path38.join(outDirAbs, ...relPosix.split("/"));
23860
- await mkdir27(path38.dirname(dstAbs), { recursive: true });
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 writeFile26(dstAbs, content);
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(path38.join(outDirAbs, "manifest.json"), manifest);
24244
+ await writeJsonFile(path39.join(outDirAbs, "manifest.json"), manifest);
23875
24245
  }
23876
24246
  async function looksLikeEngramMdExport(fromDir) {
23877
- const dirAbs = path38.resolve(fromDir);
24247
+ const dirAbs = path39.resolve(fromDir);
23878
24248
  try {
23879
- const raw = await readFile27(path38.join(dirAbs, "manifest.json"), "utf-8");
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 path39 from "path";
23889
- import { mkdir as mkdir28, readdir as readdir19, rm as rm5 } from "fs/promises";
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 = path39.resolve(opts.outDir);
23895
- await mkdir28(outDirAbs, { recursive: true });
24264
+ const outDirAbs = path40.resolve(opts.outDir);
24265
+ await mkdir29(outDirAbs, { recursive: true });
23896
24266
  const ts = timestampDirName(/* @__PURE__ */ new Date());
23897
- const backupDir = path39.join(outDirAbs, ts);
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(path39.join(outDirAbs, name), { recursive: true, force: true });
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 path40 from "path";
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 = path40.resolve(opts.memoryDir);
23957
- const outAbs = path40.resolve(opts.outFile);
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 path41 from "path";
23990
- import { mkdir as mkdir29, writeFile as writeFile27 } from "fs/promises";
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 = path41.resolve(opts.fromDir);
24024
- const bundlePath = path41.join(fromDirAbs, "bundle.json");
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 = path41.resolve(opts.targetMemoryDir);
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 ? path41.resolve(opts.workspaceDir) : null : memDirAbs;
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 = path41.join(targetBase, relFs);
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 mkdir29(path41.dirname(w.abs), { recursive: true });
24062
- await writeFile27(w.abs, w.content, "utf-8");
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 = path41.resolve(fromDir);
24437
+ const dir = path42.resolve(fromDir);
24068
24438
  return Promise.all([
24069
- fileExists(path41.join(dir, "manifest.json")),
24070
- fileExists(path41.join(dir, "bundle.json"))
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 path42 from "path";
24445
+ import path43 from "path";
24076
24446
  import Database2 from "better-sqlite3";
24077
- import { mkdir as mkdir30, writeFile as writeFile28 } from "fs/promises";
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 = path42.resolve(opts.targetMemoryDir);
24084
- const fromAbs = path42.resolve(opts.fromFile);
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 = path42.join(memDirAbs, relFs);
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 mkdir30(path42.dirname(w.abs), { recursive: true });
24123
- await writeFile28(w.abs, w.content, "utf-8");
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 path43 from "path";
24130
- import { mkdir as mkdir31, readFile as readFile29, writeFile as writeFile29 } from "fs/promises";
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 = path43.resolve(opts.fromDir);
24137
- const targetAbs = path43.resolve(opts.targetMemoryDir);
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 = path43.join(targetAbs, fromPosixRelPath(relPosix));
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 mkdir31(path43.dirname(w.abs), { recursive: true });
24168
- await writeFile29(w.abs, w.content, "utf-8");
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 path44 from "path";
24544
+ import path45 from "path";
24175
24545
  import { stat as stat9 } from "fs/promises";
24176
24546
  async function detectImportFormat(fromPath) {
24177
- const abs = path44.resolve(fromPath);
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 path45 from "path";
24663
- import { mkdir as mkdir32, readdir as readdir20, readFile as readFile30, unlink as unlink6, writeFile as writeFile30 } from "fs/promises";
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 ? path45.join(relPrefix, entry.name) : entry.name;
24690
- const full = path45.join(root, entry.name);
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", path45.join("state", "tool-usage"), path45.join("summaries", "hourly")];
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 = path45.join(memoryDir, relRoot);
25073
+ const absRoot = path46.join(memoryDir, relRoot);
24704
25074
  const files = await listFilesRecursive2(absRoot);
24705
25075
  for (const fileRel of files) {
24706
- const filename = path45.basename(fileRel);
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: path45.join(absRoot, fileRel),
24712
- relativePath: path45.join(relRoot, fileRel)
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 = path45.join(options.memoryDir, "archive", "observations", stamp);
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 mkdir32(archiveRoot, { recursive: true });
25106
+ await mkdir33(archiveRoot, { recursive: true });
24737
25107
  for (const candidate of candidates) {
24738
- const archivePath = path45.join(archiveRoot, candidate.relativePath);
24739
- const archiveDir = path45.dirname(archivePath);
24740
- await mkdir32(archiveDir, { recursive: true });
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 writeFile30(archivePath, raw);
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 path47 from "path";
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 path46 from "path";
24768
- import { mkdir as mkdir33, readFile as readFile31, writeFile as writeFile31 } from "fs/promises";
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 = path46.join(options.memoryDir, "archive", "observations", stamp);
24780
- let backupPath = path46.join(
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 mkdir33(path46.dirname(backupPath), { recursive: true });
24789
- await writeFile31(backupPath, existing, "utf-8");
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 mkdir33(path46.dirname(options.outputPath), { recursive: true });
24806
- await writeFile31(options.outputPath, lines.length > 0 ? `${lines.join("\n")}
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 = path47.join(root, entry.name);
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 = path47.join(options.memoryDir, "transcripts");
24889
- const outputPath = path47.join(
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 path48 from "path";
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 = path48.join(options.memoryDir, "state", "observation-ledger");
24990
- const outputPath = path48.join(ledgerRoot, "rebuilt-observations.jsonl");
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) => path48.join("state", "observation-ledger", 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 = path48.join(ledgerRoot, file);
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 mkdir34, readdir as readdir23, realpath as realpath2, stat as stat11 } from "fs/promises";
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 path49 from "path";
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 = path49.resolve(dir);
25227
- await mkdir34(resolved, { recursive: true });
25596
+ const resolved = path50.resolve(dir);
25597
+ await mkdir35(resolved, { recursive: true });
25228
25598
  const canonical = await realpath2(resolved);
25229
- const alias = path49.basename(canonical) || "root";
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 = path49.posix.normalize(decodedPath);
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 = path49.resolve(root.absolute, ...relative);
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 === path49.parse(root).root) {
25847
+ if (root === path50.parse(root).root) {
25478
25848
  return target.startsWith(root);
25479
25849
  }
25480
- return target.startsWith(`${root}${path49.sep}`);
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 path51 from "path";
25865
+ import path52 from "path";
25496
25866
 
25497
25867
  // src/graph-dashboard-parser.ts
25498
- import path50 from "path";
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 path50.join(memoryDir, "state", "graphs", `${type}.jsonl`);
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 ?? path51.join(process.cwd(), "dashboard", "public");
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, path51.join(this.publicDir, "app.js"), "application/javascript; charset=utf-8");
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, path51.join(this.publicDir, "index.html"), "text/html; charset=utf-8");
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 = path51.join(this.memoryDir, "state", "graphs");
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 path52 from "path";
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 = path52.join(options.repoRoot, "openclaw.plugin.json");
26054
- const packageJsonPath = path52.join(options.repoRoot, "package.json");
26055
- const indexPath = path52.join(options.repoRoot, "src", "index.ts");
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));