@joshuaswarren/openclaw-engram 8.3.20 → 8.3.22

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
@@ -8238,7 +8238,10 @@ var LastRecallStore = class {
8238
8238
  recordedAt: now,
8239
8239
  queryHash,
8240
8240
  queryLen: opts.query.length,
8241
- memoryIds: opts.memoryIds
8241
+ memoryIds: opts.memoryIds,
8242
+ identityInjectionMode: opts.identityInjection?.mode,
8243
+ identityInjectedChars: opts.identityInjection?.injectedChars,
8244
+ identityInjectionTruncated: opts.identityInjection?.truncated
8242
8245
  };
8243
8246
  this.state[opts.sessionKey] = snapshot;
8244
8247
  const keys = Object.keys(this.state);
@@ -10562,7 +10565,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
10562
10565
  };
10563
10566
 
10564
10567
  // src/compounding/engine.ts
10565
- import { mkdir as mkdir15, readFile as readFile15, writeFile as writeFile14 } from "fs/promises";
10568
+ import { mkdir as mkdir15, readFile as readFile15, readdir as readdir10, writeFile as writeFile14 } from "fs/promises";
10566
10569
  import path21 from "path";
10567
10570
  import os4 from "os";
10568
10571
  function isoWeekId(d) {
@@ -10574,6 +10577,22 @@ function isoWeekId(d) {
10574
10577
  const yyyy = dt.getUTCFullYear();
10575
10578
  return `${yyyy}-W${String(week).padStart(2, "0")}`;
10576
10579
  }
10580
+ function isoMonthId(d) {
10581
+ return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
10582
+ }
10583
+ function monthIdFromIsoWeek(weekId) {
10584
+ const match = weekId.match(/^(\d{4})-W(\d{2})$/);
10585
+ if (!match) return isoMonthId(/* @__PURE__ */ new Date());
10586
+ const year = Number(match[1]);
10587
+ const week = Number(match[2]);
10588
+ const jan4 = new Date(Date.UTC(year, 0, 4));
10589
+ const jan4Day = jan4.getUTCDay() || 7;
10590
+ const isoWeekOneMonday = new Date(jan4);
10591
+ isoWeekOneMonday.setUTCDate(jan4.getUTCDate() - (jan4Day - 1));
10592
+ const monday = new Date(isoWeekOneMonday);
10593
+ monday.setUTCDate(isoWeekOneMonday.getUTCDate() + (week - 1) * 7);
10594
+ return isoMonthId(monday);
10595
+ }
10577
10596
  function sharedContextDir(config) {
10578
10597
  if (typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0) {
10579
10598
  return config.sharedContextDir;
@@ -10586,10 +10605,20 @@ var CompoundingEngine = class {
10586
10605
  this.weeklyDir = path21.join(config.memoryDir, "compounding", "weekly");
10587
10606
  this.mistakesPath = path21.join(config.memoryDir, "compounding", "mistakes.json");
10588
10607
  this.feedbackInboxPath = path21.join(sharedContextDir(config), "feedback", "inbox.jsonl");
10608
+ this.identityAnchorPath = path21.join(config.memoryDir, "identity", "identity-anchor.md");
10609
+ this.identityIncidentsDir = path21.join(config.memoryDir, "identity", "incidents");
10610
+ this.identityAuditWeeklyDir = path21.join(config.memoryDir, "identity", "audits", "weekly");
10611
+ this.identityAuditMonthlyDir = path21.join(config.memoryDir, "identity", "audits", "monthly");
10612
+ this.identityImprovementLoopsPath = path21.join(config.memoryDir, "identity", "improvement-loops.md");
10589
10613
  }
10590
10614
  weeklyDir;
10591
10615
  mistakesPath;
10592
10616
  feedbackInboxPath;
10617
+ identityAnchorPath;
10618
+ identityIncidentsDir;
10619
+ identityAuditWeeklyDir;
10620
+ identityAuditMonthlyDir;
10621
+ identityImprovementLoopsPath;
10593
10622
  async ensureDirs() {
10594
10623
  await mkdir15(this.weeklyDir, { recursive: true });
10595
10624
  await mkdir15(path21.dirname(this.mistakesPath), { recursive: true });
@@ -10599,13 +10628,77 @@ var CompoundingEngine = class {
10599
10628
  const weekId = opts?.weekId ?? isoWeekId(/* @__PURE__ */ new Date());
10600
10629
  const entries = await this.readFeedbackEntriesForWeek(weekId);
10601
10630
  const mistakes = this.buildMistakes(entries);
10631
+ const continuity = await this.readContinuityAuditReferences(weekId);
10602
10632
  const reportPath = path21.join(this.weeklyDir, `${weekId}.md`);
10603
- const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns);
10633
+ const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns, continuity);
10604
10634
  await writeFile14(reportPath, md, "utf-8");
10605
10635
  await writeFile14(this.mistakesPath, JSON.stringify(mistakes, null, 2) + "\n", "utf-8");
10606
10636
  log.info(`compounding: wrote weekly=${reportPath} mistakes=${this.mistakesPath}`);
10607
10637
  return { weekId, reportPath, mistakesCount: mistakes.patterns.length };
10608
10638
  }
10639
+ async synthesizeContinuityAudit(opts) {
10640
+ const period = opts?.period === "monthly" ? "monthly" : "weekly";
10641
+ const key = opts?.key?.trim() || (period === "weekly" ? isoWeekId(/* @__PURE__ */ new Date()) : isoMonthId(/* @__PURE__ */ new Date()));
10642
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
10643
+ const [anchorPresent, improvementLoopPresent, incidents, mistakes] = await Promise.all([
10644
+ this.readNonEmptyFile(this.identityAnchorPath),
10645
+ this.readNonEmptyFile(this.identityImprovementLoopsPath),
10646
+ this.readContinuityIncidents(),
10647
+ this.readMistakes()
10648
+ ]);
10649
+ const openIncidents = incidents.filter((i) => i.state === "open");
10650
+ const closedIncidents = incidents.filter((i) => i.state === "closed");
10651
+ const hardeningCandidates = [];
10652
+ if (!anchorPresent) {
10653
+ hardeningCandidates.push("Create/update identity anchor baseline and verify recovery injection path.");
10654
+ }
10655
+ if (openIncidents.length > 0) {
10656
+ hardeningCandidates.push(
10657
+ `Close or downgrade ${openIncidents.length} open continuity incident${openIncidents.length === 1 ? "" : "s"}.`
10658
+ );
10659
+ }
10660
+ if (!improvementLoopPresent) {
10661
+ hardeningCandidates.push("Initialize continuity improvement-loops register with cadence and kill conditions.");
10662
+ }
10663
+ if ((mistakes?.patterns.length ?? 0) > 0) {
10664
+ hardeningCandidates.push("Review latest compounding mistakes and convert one pattern into preventive continuity rule.");
10665
+ }
10666
+ const nextAction = hardeningCandidates[0] ?? "No critical drift detected; keep weekly/monthly continuity audit cadence.";
10667
+ const lines = [
10668
+ `# Continuity Audit \u2014 ${period} ${key}`,
10669
+ "",
10670
+ `Generated: ${nowIso}`,
10671
+ `Scope: ${period}`,
10672
+ "",
10673
+ "## Signal Summary",
10674
+ `- Identity anchor present: ${anchorPresent ? "yes" : "no"}`,
10675
+ `- Improvement-loop register present: ${improvementLoopPresent ? "yes" : "no"}`,
10676
+ `- Open incidents: ${openIncidents.length}`,
10677
+ `- Closed incidents: ${closedIncidents.length}`,
10678
+ `- Compounding mistake patterns: ${mistakes?.patterns.length ?? 0}`,
10679
+ "",
10680
+ "## Drift Checks",
10681
+ `- Identity anchor drift: ${anchorPresent ? "pass" : "needs attention"}`,
10682
+ `- Incident backlog: ${openIncidents.length === 0 ? "pass" : "needs attention"}`,
10683
+ `- Improvement-loop coverage: ${improvementLoopPresent ? "pass" : "needs attention"}`,
10684
+ "",
10685
+ "## Stale Rule Detection",
10686
+ `- Open incidents older than closure window: ${openIncidents.length > 0 ? "possible" : "none detected"}`,
10687
+ `- Preventive rule coverage on closed incidents: ${closedIncidents.some((i) => (i.preventiveRule ?? "").trim().length > 0) ? "present" : "not detected"}`,
10688
+ "",
10689
+ "## Next Hardening Action",
10690
+ `- ${nextAction}`,
10691
+ "",
10692
+ "## Open Incident IDs",
10693
+ ...openIncidents.length > 0 ? openIncidents.slice(0, 20).map((i) => `- ${i.id}`) : ["- (none)"],
10694
+ ""
10695
+ ];
10696
+ const dir = period === "weekly" ? this.identityAuditWeeklyDir : this.identityAuditMonthlyDir;
10697
+ await mkdir15(dir, { recursive: true });
10698
+ const reportPath = path21.join(dir, `${key}.md`);
10699
+ await writeFile14(reportPath, lines.join("\n"), "utf-8");
10700
+ return { period, key, reportPath };
10701
+ }
10609
10702
  async readMistakes() {
10610
10703
  try {
10611
10704
  const raw = await readFile15(this.mistakesPath, "utf-8");
@@ -10650,7 +10743,7 @@ var CompoundingEngine = class {
10650
10743
  const uniq = Array.from(new Set(patterns)).slice(0, 500);
10651
10744
  return { updatedAt: (/* @__PURE__ */ new Date()).toISOString(), patterns: uniq };
10652
10745
  }
10653
- formatWeeklyReport(weekId, entries, patterns) {
10746
+ formatWeeklyReport(weekId, entries, patterns, continuity) {
10654
10747
  const byAgent = /* @__PURE__ */ new Map();
10655
10748
  for (const e of entries) {
10656
10749
  const list = byAgent.get(e.agent) ?? [];
@@ -10689,8 +10782,61 @@ var CompoundingEngine = class {
10689
10782
  for (const p of patterns.slice(0, 100)) lines.push(`- ${p}`);
10690
10783
  }
10691
10784
  lines.push("");
10785
+ if (this.config.continuityAuditEnabled) {
10786
+ lines.push("## Continuity Audits");
10787
+ if (continuity.weeklyPath) {
10788
+ lines.push(`- weekly: ${continuity.weeklyPath}`);
10789
+ } else {
10790
+ lines.push(`- weekly: (missing for ${weekId})`);
10791
+ }
10792
+ if (continuity.monthlyPath) {
10793
+ lines.push(`- monthly: ${continuity.monthlyPath}`);
10794
+ } else {
10795
+ lines.push(`- monthly: (missing for ${continuity.monthId})`);
10796
+ }
10797
+ lines.push("");
10798
+ }
10692
10799
  return lines.join("\n");
10693
10800
  }
10801
+ async readNonEmptyFile(filePath) {
10802
+ try {
10803
+ const raw = await readFile15(filePath, "utf-8");
10804
+ return raw.trim().length > 0;
10805
+ } catch {
10806
+ return false;
10807
+ }
10808
+ }
10809
+ async readContinuityIncidents() {
10810
+ const incidents = [];
10811
+ try {
10812
+ const names = await readdir10(this.identityIncidentsDir);
10813
+ const files = names.filter((n) => n.endsWith(".md")).sort().reverse();
10814
+ for (const file of files) {
10815
+ const filePath = path21.join(this.identityIncidentsDir, file);
10816
+ try {
10817
+ const raw = await readFile15(filePath, "utf-8");
10818
+ const parsed = parseContinuityIncident(raw);
10819
+ if (parsed) incidents.push(parsed);
10820
+ } catch {
10821
+ }
10822
+ }
10823
+ } catch {
10824
+ }
10825
+ return incidents;
10826
+ }
10827
+ async readContinuityAuditReferences(weekId) {
10828
+ const monthId = monthIdFromIsoWeek(weekId);
10829
+ const weeklyPath = path21.join(this.identityAuditWeeklyDir, `${weekId}.md`);
10830
+ const monthlyPath = path21.join(this.identityAuditMonthlyDir, `${monthId}.md`);
10831
+ const weeklyExists = await this.readNonEmptyFile(weeklyPath);
10832
+ const monthlyExists = await this.readNonEmptyFile(monthlyPath);
10833
+ return {
10834
+ weekId,
10835
+ monthId,
10836
+ weeklyPath: weeklyExists ? weeklyPath : null,
10837
+ monthlyPath: monthlyExists ? monthlyPath : null
10838
+ };
10839
+ }
10694
10840
  };
10695
10841
 
10696
10842
  // src/orchestrator.ts
@@ -10828,6 +10974,22 @@ function resolveEffectiveRecallMode(options) {
10828
10974
  }
10829
10975
  return plannedMode;
10830
10976
  }
10977
+ function hasIdentityRecoveryIntent(prompt) {
10978
+ const text = typeof prompt === "string" ? prompt.toLowerCase() : "";
10979
+ if (!text) return false;
10980
+ return /\b(identity|continuity|recover(?:y|ing|ed)?|incident|drift|restore|regress(?:ion|ed|ing)?)\b/i.test(
10981
+ text
10982
+ );
10983
+ }
10984
+ function resolveEffectiveIdentityInjectionMode(options) {
10985
+ if (options.configuredMode === "recovery_only" && !hasIdentityRecoveryIntent(options.prompt)) {
10986
+ return { mode: "recovery_only", shouldInject: false };
10987
+ }
10988
+ if (options.recallMode === "minimal" && options.configuredMode === "full") {
10989
+ return { mode: "minimal", shouldInject: true };
10990
+ }
10991
+ return { mode: options.configuredMode, shouldInject: true };
10992
+ }
10831
10993
  function computeArtifactCandidateFetchLimit(targetCount) {
10832
10994
  const cappedTarget = Math.max(0, targetCount);
10833
10995
  if (cappedTarget === 0) return 0;
@@ -11679,6 +11841,9 @@ var Orchestrator = class _Orchestrator {
11679
11841
  let impressionRecorded = false;
11680
11842
  let recallSource = "none";
11681
11843
  let recalledMemoryCount = 0;
11844
+ let identityInjectionModeUsed = "none";
11845
+ let identityInjectedChars = 0;
11846
+ let identityInjectionTruncated = false;
11682
11847
  timings.queryPolicy = `${queryPolicy.promptShape}/${queryPolicy.retrievalBudgetMode}${queryPolicy.skipConversationRecall ? "/skip-conv" : ""}`;
11683
11848
  const recallMode = resolveEffectiveRecallMode({
11684
11849
  plannerEnabled: this.config.recallPlannerEnabled,
@@ -11723,6 +11888,9 @@ var Orchestrator = class _Orchestrator {
11723
11888
  recalledMemoryCount,
11724
11889
  injected: false,
11725
11890
  contextChars: 0,
11891
+ identityInjectionMode: identityInjectionModeUsed,
11892
+ identityInjectedChars,
11893
+ identityInjectionTruncated,
11726
11894
  durationMs: Date.now() - recallStart,
11727
11895
  timings: { ...timings }
11728
11896
  });
@@ -11756,6 +11924,16 @@ var Orchestrator = class _Orchestrator {
11756
11924
  timings.profile = `${Date.now() - t0}ms`;
11757
11925
  return profile2 || null;
11758
11926
  })();
11927
+ const identityContinuityPromise = (async () => {
11928
+ const t0 = Date.now();
11929
+ const section = await this.buildIdentityContinuitySection({
11930
+ storage: profileStorage,
11931
+ recallMode,
11932
+ prompt: retrievalQuery
11933
+ });
11934
+ timings.identityContinuity = `${Date.now() - t0}ms`;
11935
+ return section;
11936
+ })();
11759
11937
  const knowledgeIndexPromise = (async () => {
11760
11938
  if (!this.config.knowledgeIndexEnabled) return null;
11761
11939
  const t0 = Date.now();
@@ -11813,9 +11991,10 @@ var Orchestrator = class _Orchestrator {
11813
11991
  timings.qmd = `${Date.now() - t0}ms`;
11814
11992
  return { memoryResultsLists: [filteredResults], globalResults: [] };
11815
11993
  })();
11816
- const [sharedCtx, profile, kiResult, artifacts, qmdResult] = await Promise.all([
11994
+ const [sharedCtx, profile, identityContinuity, kiResult, artifacts, qmdResult] = await Promise.all([
11817
11995
  sharedContextPromise,
11818
11996
  profilePromise,
11997
+ identityContinuityPromise,
11819
11998
  knowledgeIndexPromise,
11820
11999
  artifactsPromise,
11821
12000
  qmdPromise
@@ -11824,6 +12003,12 @@ var Orchestrator = class _Orchestrator {
11824
12003
  if (profile) sections.push(`## User Profile
11825
12004
 
11826
12005
  ${profile}`);
12006
+ if (identityContinuity) {
12007
+ sections.push(identityContinuity.section);
12008
+ identityInjectionModeUsed = identityContinuity.mode;
12009
+ identityInjectedChars = identityContinuity.injectedChars;
12010
+ identityInjectionTruncated = identityContinuity.truncated;
12011
+ }
11827
12012
  if (kiResult?.result) {
11828
12013
  sections.push(kiResult.result);
11829
12014
  log.debug(`Knowledge Index: ${kiResult.result.split("\n").length - 4} entities, ${kiResult.result.length} chars${kiResult.cached ? " (cached)" : ""}`);
@@ -11935,7 +12120,12 @@ ${tmtNode.summary}`);
11935
12120
  results: memoryResults,
11936
12121
  sections,
11937
12122
  retrievalQuery,
11938
- sessionKey
12123
+ sessionKey,
12124
+ identityInjection: {
12125
+ mode: identityInjectionModeUsed,
12126
+ injectedChars: identityInjectedChars,
12127
+ truncated: identityInjectionTruncated
12128
+ }
11939
12129
  });
11940
12130
  impressionRecorded = true;
11941
12131
  } else {
@@ -11958,7 +12148,12 @@ ${tmtNode.summary}`);
11958
12148
  results: scoped,
11959
12149
  sections,
11960
12150
  retrievalQuery,
11961
- sessionKey
12151
+ sessionKey,
12152
+ identityInjection: {
12153
+ mode: identityInjectionModeUsed,
12154
+ injectedChars: identityInjectedChars,
12155
+ truncated: identityInjectionTruncated
12156
+ }
11962
12157
  });
11963
12158
  impressionRecorded = true;
11964
12159
  } else {
@@ -11975,7 +12170,12 @@ ${tmtNode.summary}`);
11975
12170
  results: longTerm,
11976
12171
  sections,
11977
12172
  retrievalQuery,
11978
- sessionKey
12173
+ sessionKey,
12174
+ identityInjection: {
12175
+ mode: identityInjectionModeUsed,
12176
+ injectedChars: identityInjectedChars,
12177
+ truncated: identityInjectionTruncated
12178
+ }
11979
12179
  });
11980
12180
  impressionRecorded = true;
11981
12181
  }
@@ -12021,7 +12221,12 @@ ${tmtNode.summary}`);
12021
12221
  results: scoped,
12022
12222
  sections,
12023
12223
  retrievalQuery,
12024
- sessionKey
12224
+ sessionKey,
12225
+ identityInjection: {
12226
+ mode: identityInjectionModeUsed,
12227
+ injectedChars: identityInjectedChars,
12228
+ truncated: identityInjectionTruncated
12229
+ }
12025
12230
  });
12026
12231
  impressionRecorded = true;
12027
12232
  } else {
@@ -12056,7 +12261,12 @@ ${tmtNode.summary}`);
12056
12261
  results: recent,
12057
12262
  sections,
12058
12263
  retrievalQuery,
12059
- sessionKey
12264
+ sessionKey,
12265
+ identityInjection: {
12266
+ mode: identityInjectionModeUsed,
12267
+ injectedChars: identityInjectedChars,
12268
+ truncated: identityInjectionTruncated
12269
+ }
12060
12270
  });
12061
12271
  impressionRecorded = true;
12062
12272
  } else {
@@ -12073,7 +12283,12 @@ ${tmtNode.summary}`);
12073
12283
  results: longTerm,
12074
12284
  sections,
12075
12285
  retrievalQuery,
12076
- sessionKey
12286
+ sessionKey,
12287
+ identityInjection: {
12288
+ mode: identityInjectionModeUsed,
12289
+ injectedChars: identityInjectedChars,
12290
+ truncated: identityInjectionTruncated
12291
+ }
12077
12292
  });
12078
12293
  impressionRecorded = true;
12079
12294
  }
@@ -12092,7 +12307,12 @@ ${tmtNode.summary}`);
12092
12307
  results: longTerm,
12093
12308
  sections,
12094
12309
  retrievalQuery,
12095
- sessionKey
12310
+ sessionKey,
12311
+ identityInjection: {
12312
+ mode: identityInjectionModeUsed,
12313
+ injectedChars: identityInjectedChars,
12314
+ truncated: identityInjectionTruncated
12315
+ }
12096
12316
  });
12097
12317
  impressionRecorded = true;
12098
12318
  }
@@ -12231,7 +12451,16 @@ _Context: ${topQuestion.context}_`);
12231
12451
  const timingParts = Object.entries(timings).map(([k, v]) => `${k}=${v}`).join(", ");
12232
12452
  log.debug(`recall: ${timingParts}`);
12233
12453
  if (!impressionRecorded && sessionKey && this.config.recordEmptyRecallImpressions) {
12234
- this.lastRecall.record({ sessionKey, query: retrievalQuery, memoryIds: [] }).catch((err) => log.debug(`last recall record failed: ${err}`));
12454
+ this.lastRecall.record({
12455
+ sessionKey,
12456
+ query: retrievalQuery,
12457
+ memoryIds: [],
12458
+ identityInjection: {
12459
+ mode: identityInjectionModeUsed,
12460
+ injectedChars: identityInjectedChars,
12461
+ truncated: identityInjectionTruncated
12462
+ }
12463
+ }).catch((err) => log.debug(`last recall record failed: ${err}`));
12235
12464
  }
12236
12465
  const context = sections.length === 0 ? "" : sections.join("\n\n---\n\n");
12237
12466
  this.emitTrace({
@@ -12252,6 +12481,9 @@ _Context: ${topQuestion.context}_`);
12252
12481
  recalledMemoryCount,
12253
12482
  injected: context.length > 0,
12254
12483
  contextChars: context.length,
12484
+ identityInjectionMode: identityInjectionModeUsed,
12485
+ identityInjectedChars,
12486
+ identityInjectionTruncated,
12255
12487
  durationMs: Date.now() - recallStart,
12256
12488
  timings: { ...timings }
12257
12489
  });
@@ -13347,6 +13579,86 @@ ${snippet}`;
13347
13579
 
13348
13580
  ${lines.join("\n\n")}`;
13349
13581
  }
13582
+ summarizeIdentityText(raw, maxLines, maxChars) {
13583
+ const lines = raw.replace(/\r/g, "").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
13584
+ const compact = lines.slice(0, Math.max(1, maxLines)).join(" ");
13585
+ if (compact.length <= maxChars) return compact;
13586
+ return `${compact.slice(0, Math.max(0, maxChars - 1))}\u2026`;
13587
+ }
13588
+ formatOpenIncidentLine(incident, includeDetails) {
13589
+ const base = `[${incident.id}] ${incident.symptom.trim()}`;
13590
+ if (!includeDetails) return `- ${base}`;
13591
+ const parts = [base];
13592
+ if (incident.suspectedCause) parts.push(`cause: ${incident.suspectedCause.trim()}`);
13593
+ if (incident.triggerWindow) parts.push(`window: ${incident.triggerWindow.trim()}`);
13594
+ return `- ${parts.join(" | ")}`;
13595
+ }
13596
+ trimIdentitySection(content, maxChars) {
13597
+ if (maxChars <= 0) return { text: "", truncated: false };
13598
+ if (content.length <= maxChars) return { text: content, truncated: false };
13599
+ const suffix = "\n\n...(identity continuity trimmed)";
13600
+ if (maxChars <= suffix.length) {
13601
+ return { text: content.slice(0, maxChars), truncated: true };
13602
+ }
13603
+ const headroom = Math.max(0, maxChars - suffix.length);
13604
+ return { text: `${content.slice(0, headroom)}${suffix}`, truncated: true };
13605
+ }
13606
+ async buildIdentityContinuitySection(options) {
13607
+ if (!this.config.identityContinuityEnabled) return null;
13608
+ if (this.config.identityMaxInjectChars <= 0) return null;
13609
+ const resolved = resolveEffectiveIdentityInjectionMode({
13610
+ configuredMode: this.config.identityInjectionMode,
13611
+ recallMode: options.recallMode,
13612
+ prompt: options.prompt
13613
+ });
13614
+ if (!resolved.shouldInject) return null;
13615
+ const [anchorRaw, loopsRaw, incidents] = await Promise.all([
13616
+ options.storage.readIdentityAnchor(),
13617
+ options.storage.readIdentityImprovementLoops(),
13618
+ options.storage.readContinuityIncidents(200)
13619
+ ]);
13620
+ const openIncidents = incidents.filter((incident) => incident.state === "open");
13621
+ const lines = [];
13622
+ if (resolved.mode === "full") {
13623
+ lines.push("## Identity Continuity");
13624
+ if (anchorRaw && anchorRaw.trim().length > 0) {
13625
+ lines.push("", "### Anchor", "", anchorRaw.trim());
13626
+ }
13627
+ if (loopsRaw && loopsRaw.trim().length > 0) {
13628
+ lines.push("", "### Improvement Loops", "", loopsRaw.trim());
13629
+ }
13630
+ lines.push("", "### Open Incidents", "");
13631
+ if (openIncidents.length === 0) {
13632
+ lines.push("- none");
13633
+ } else {
13634
+ lines.push(
13635
+ ...openIncidents.slice(0, 5).map((incident) => this.formatOpenIncidentLine(incident, true))
13636
+ );
13637
+ }
13638
+ } else {
13639
+ const anchorSummary = anchorRaw ? this.summarizeIdentityText(anchorRaw, 3, 320) : "";
13640
+ const loopsSummary = loopsRaw ? this.summarizeIdentityText(loopsRaw, 2, 240) : "";
13641
+ lines.push("## Identity Continuity Signals", "");
13642
+ if (anchorSummary) lines.push(`- anchor: ${anchorSummary}`);
13643
+ if (loopsSummary) lines.push(`- loops: ${loopsSummary}`);
13644
+ if (openIncidents.length === 0) {
13645
+ lines.push("- incidents: 0 open");
13646
+ } else {
13647
+ lines.push(`- incidents: ${openIncidents.length} open`);
13648
+ lines.push(...openIncidents.slice(0, 2).map((incident) => this.formatOpenIncidentLine(incident, false)));
13649
+ }
13650
+ }
13651
+ const body = lines.join("\n").trim();
13652
+ if (!body) return null;
13653
+ const { text, truncated } = this.trimIdentitySection(body, this.config.identityMaxInjectChars);
13654
+ if (!text) return null;
13655
+ return {
13656
+ section: text,
13657
+ mode: resolved.mode,
13658
+ injectedChars: text.length,
13659
+ truncated
13660
+ };
13661
+ }
13350
13662
  emitTrace(event) {
13351
13663
  try {
13352
13664
  const cb = globalThis.__openclawEngramTrace;
@@ -13360,7 +13672,12 @@ ${lines.join("\n\n")}`;
13360
13672
  this.trackMemoryAccess(memoryIds);
13361
13673
  if (options.sessionKey) {
13362
13674
  const unique = Array.from(new Set(memoryIds)).slice(0, 40);
13363
- this.lastRecall.record({ sessionKey: options.sessionKey, query: options.retrievalQuery, memoryIds: unique }).catch((err) => log.debug(`last recall record failed: ${err}`));
13675
+ this.lastRecall.record({
13676
+ sessionKey: options.sessionKey,
13677
+ query: options.retrievalQuery,
13678
+ memoryIds: unique,
13679
+ identityInjection: options.identityInjection
13680
+ }).catch((err) => log.debug(`last recall record failed: ${err}`));
13364
13681
  }
13365
13682
  options.sections.push(this.formatQmdResults(options.title, options.results));
13366
13683
  }
@@ -14011,6 +14328,57 @@ ${formatted}`
14011
14328
  },
14012
14329
  { name: "memory_search" }
14013
14330
  );
14331
+ api.registerTool(
14332
+ {
14333
+ name: "continuity_audit_generate",
14334
+ label: "Generate Continuity Audit",
14335
+ description: "Generate a deterministic identity continuity audit report (weekly/monthly) and persist it under identity/audits.",
14336
+ parameters: Type.Object({
14337
+ period: Type.Optional(
14338
+ Type.String({
14339
+ enum: ["weekly", "monthly"],
14340
+ description: "Audit period. Defaults to weekly."
14341
+ })
14342
+ ),
14343
+ key: Type.Optional(
14344
+ Type.String({
14345
+ description: "Optional period key (weekly: YYYY-Www, monthly: YYYY-MM). Defaults to current period."
14346
+ })
14347
+ )
14348
+ }),
14349
+ async execute(_toolCallId, params) {
14350
+ if (!orchestrator.config.identityContinuityEnabled) {
14351
+ return toolResult(
14352
+ "Identity continuity is disabled. Enable `identityContinuityEnabled: true` to generate continuity audits."
14353
+ );
14354
+ }
14355
+ if (!orchestrator.config.continuityAuditEnabled) {
14356
+ return toolResult(
14357
+ "Continuity audits are disabled. Enable `continuityAuditEnabled: true` to generate continuity audits."
14358
+ );
14359
+ }
14360
+ if (!orchestrator.compounding) {
14361
+ return toolResult(
14362
+ "Compounding engine is disabled. Enable `compoundingEnabled: true` to generate continuity audits."
14363
+ );
14364
+ }
14365
+ const period = params.period === "monthly" ? "monthly" : "weekly";
14366
+ const key = typeof params.key === "string" ? params.key : void 0;
14367
+ const audit = await orchestrator.compounding.synthesizeContinuityAudit({
14368
+ period,
14369
+ key
14370
+ });
14371
+ return toolResult(
14372
+ `OK
14373
+
14374
+ period: ${audit.period}
14375
+ key: ${audit.key}
14376
+ report: ${audit.reportPath}`
14377
+ );
14378
+ }
14379
+ },
14380
+ { name: "continuity_audit_generate" }
14381
+ );
14014
14382
  api.registerTool(
14015
14383
  {
14016
14384
  name: "continuity_incident_open",
@@ -14303,6 +14671,7 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
14303
14671
  "",
14304
14672
  `Recorded at: ${snap.recordedAt}`,
14305
14673
  `Query hash: ${snap.queryHash} (len=${snap.queryLen})`,
14674
+ `Identity injection: mode=${snap.identityInjectionMode ?? "none"}, chars=${snap.identityInjectedChars ?? 0}, truncated=${snap.identityInjectionTruncated === true ? "yes" : "no"}`,
14306
14675
  `Memories (${snap.memoryIds.length}):`,
14307
14676
  ...snap.memoryIds.map((id) => `- ${id}`)
14308
14677
  ].join("\n")
@@ -15045,7 +15414,7 @@ mistakes: ${res.mistakesCount} patterns`
15045
15414
 
15046
15415
  // src/cli.ts
15047
15416
  import path33 from "path";
15048
- import { access as access2, readFile as readFile22, readdir as readdir12, unlink as unlink3 } from "fs/promises";
15417
+ import { access as access2, readFile as readFile22, readdir as readdir13, unlink as unlink3 } from "fs/promises";
15049
15418
 
15050
15419
  // src/transfer/export-json.ts
15051
15420
  import path25 from "path";
@@ -15057,7 +15426,7 @@ var EXPORT_SCHEMA_VERSION = 1;
15057
15426
 
15058
15427
  // src/transfer/fs-utils.ts
15059
15428
  import { createHash as createHash6 } from "crypto";
15060
- import { mkdir as mkdir17, readdir as readdir10, readFile as readFile17, stat as stat3, writeFile as writeFile16 } from "fs/promises";
15429
+ import { mkdir as mkdir17, readdir as readdir11, readFile as readFile17, stat as stat3, writeFile as writeFile16 } from "fs/promises";
15061
15430
  import path24 from "path";
15062
15431
  async function sha256File(filePath) {
15063
15432
  const buf = await readFile17(filePath);
@@ -15080,7 +15449,7 @@ async function readJsonFile(filePath) {
15080
15449
  async function listFilesRecursive(rootDir) {
15081
15450
  const out = [];
15082
15451
  async function walk(dir) {
15083
- const entries = await readdir10(dir, { withFileTypes: true });
15452
+ const entries = await readdir11(dir, { withFileTypes: true });
15084
15453
  for (const ent of entries) {
15085
15454
  const fp = path24.join(dir, ent.name);
15086
15455
  if (ent.isDirectory()) {
@@ -15208,7 +15577,7 @@ async function looksLikeEngramMdExport(fromDir) {
15208
15577
 
15209
15578
  // src/transfer/backup.ts
15210
15579
  import path27 from "path";
15211
- import { mkdir as mkdir20, readdir as readdir11, rm as rm2 } from "fs/promises";
15580
+ import { mkdir as mkdir20, readdir as readdir12, rm as rm2 } from "fs/promises";
15212
15581
  function timestampDirName(now) {
15213
15582
  return now.toISOString().replace(/[:.]/g, "-");
15214
15583
  }
@@ -15229,7 +15598,7 @@ async function backupMemoryDir(opts) {
15229
15598
  return backupDir;
15230
15599
  }
15231
15600
  async function enforceRetention(outDirAbs, retentionDays) {
15232
- const entries = await readdir11(outDirAbs, { withFileTypes: true });
15601
+ const entries = await readdir12(outDirAbs, { withFileTypes: true });
15233
15602
  const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
15234
15603
  for (const ent of entries) {
15235
15604
  if (!ent.isDirectory()) continue;
@@ -15598,7 +15967,7 @@ async function readAllMemoryFiles(memoryDir) {
15598
15967
  const walk = async (dir) => {
15599
15968
  let entries;
15600
15969
  try {
15601
- entries = await readdir12(dir, { withFileTypes: true });
15970
+ entries = await readdir13(dir, { withFileTypes: true });
15602
15971
  } catch {
15603
15972
  return;
15604
15973
  }