@joshuaswarren/openclaw-engram 8.3.20 → 8.3.21

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
@@ -10562,7 +10562,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
10562
10562
  };
10563
10563
 
10564
10564
  // src/compounding/engine.ts
10565
- import { mkdir as mkdir15, readFile as readFile15, writeFile as writeFile14 } from "fs/promises";
10565
+ import { mkdir as mkdir15, readFile as readFile15, readdir as readdir10, writeFile as writeFile14 } from "fs/promises";
10566
10566
  import path21 from "path";
10567
10567
  import os4 from "os";
10568
10568
  function isoWeekId(d) {
@@ -10574,6 +10574,22 @@ function isoWeekId(d) {
10574
10574
  const yyyy = dt.getUTCFullYear();
10575
10575
  return `${yyyy}-W${String(week).padStart(2, "0")}`;
10576
10576
  }
10577
+ function isoMonthId(d) {
10578
+ return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
10579
+ }
10580
+ function monthIdFromIsoWeek(weekId) {
10581
+ const match = weekId.match(/^(\d{4})-W(\d{2})$/);
10582
+ if (!match) return isoMonthId(/* @__PURE__ */ new Date());
10583
+ const year = Number(match[1]);
10584
+ const week = Number(match[2]);
10585
+ const jan4 = new Date(Date.UTC(year, 0, 4));
10586
+ const jan4Day = jan4.getUTCDay() || 7;
10587
+ const isoWeekOneMonday = new Date(jan4);
10588
+ isoWeekOneMonday.setUTCDate(jan4.getUTCDate() - (jan4Day - 1));
10589
+ const monday = new Date(isoWeekOneMonday);
10590
+ monday.setUTCDate(isoWeekOneMonday.getUTCDate() + (week - 1) * 7);
10591
+ return isoMonthId(monday);
10592
+ }
10577
10593
  function sharedContextDir(config) {
10578
10594
  if (typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0) {
10579
10595
  return config.sharedContextDir;
@@ -10586,10 +10602,20 @@ var CompoundingEngine = class {
10586
10602
  this.weeklyDir = path21.join(config.memoryDir, "compounding", "weekly");
10587
10603
  this.mistakesPath = path21.join(config.memoryDir, "compounding", "mistakes.json");
10588
10604
  this.feedbackInboxPath = path21.join(sharedContextDir(config), "feedback", "inbox.jsonl");
10605
+ this.identityAnchorPath = path21.join(config.memoryDir, "identity", "identity-anchor.md");
10606
+ this.identityIncidentsDir = path21.join(config.memoryDir, "identity", "incidents");
10607
+ this.identityAuditWeeklyDir = path21.join(config.memoryDir, "identity", "audits", "weekly");
10608
+ this.identityAuditMonthlyDir = path21.join(config.memoryDir, "identity", "audits", "monthly");
10609
+ this.identityImprovementLoopsPath = path21.join(config.memoryDir, "identity", "improvement-loops.md");
10589
10610
  }
10590
10611
  weeklyDir;
10591
10612
  mistakesPath;
10592
10613
  feedbackInboxPath;
10614
+ identityAnchorPath;
10615
+ identityIncidentsDir;
10616
+ identityAuditWeeklyDir;
10617
+ identityAuditMonthlyDir;
10618
+ identityImprovementLoopsPath;
10593
10619
  async ensureDirs() {
10594
10620
  await mkdir15(this.weeklyDir, { recursive: true });
10595
10621
  await mkdir15(path21.dirname(this.mistakesPath), { recursive: true });
@@ -10599,13 +10625,77 @@ var CompoundingEngine = class {
10599
10625
  const weekId = opts?.weekId ?? isoWeekId(/* @__PURE__ */ new Date());
10600
10626
  const entries = await this.readFeedbackEntriesForWeek(weekId);
10601
10627
  const mistakes = this.buildMistakes(entries);
10628
+ const continuity = await this.readContinuityAuditReferences(weekId);
10602
10629
  const reportPath = path21.join(this.weeklyDir, `${weekId}.md`);
10603
- const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns);
10630
+ const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns, continuity);
10604
10631
  await writeFile14(reportPath, md, "utf-8");
10605
10632
  await writeFile14(this.mistakesPath, JSON.stringify(mistakes, null, 2) + "\n", "utf-8");
10606
10633
  log.info(`compounding: wrote weekly=${reportPath} mistakes=${this.mistakesPath}`);
10607
10634
  return { weekId, reportPath, mistakesCount: mistakes.patterns.length };
10608
10635
  }
10636
+ async synthesizeContinuityAudit(opts) {
10637
+ const period = opts?.period === "monthly" ? "monthly" : "weekly";
10638
+ const key = opts?.key?.trim() || (period === "weekly" ? isoWeekId(/* @__PURE__ */ new Date()) : isoMonthId(/* @__PURE__ */ new Date()));
10639
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
10640
+ const [anchorPresent, improvementLoopPresent, incidents, mistakes] = await Promise.all([
10641
+ this.readNonEmptyFile(this.identityAnchorPath),
10642
+ this.readNonEmptyFile(this.identityImprovementLoopsPath),
10643
+ this.readContinuityIncidents(),
10644
+ this.readMistakes()
10645
+ ]);
10646
+ const openIncidents = incidents.filter((i) => i.state === "open");
10647
+ const closedIncidents = incidents.filter((i) => i.state === "closed");
10648
+ const hardeningCandidates = [];
10649
+ if (!anchorPresent) {
10650
+ hardeningCandidates.push("Create/update identity anchor baseline and verify recovery injection path.");
10651
+ }
10652
+ if (openIncidents.length > 0) {
10653
+ hardeningCandidates.push(
10654
+ `Close or downgrade ${openIncidents.length} open continuity incident${openIncidents.length === 1 ? "" : "s"}.`
10655
+ );
10656
+ }
10657
+ if (!improvementLoopPresent) {
10658
+ hardeningCandidates.push("Initialize continuity improvement-loops register with cadence and kill conditions.");
10659
+ }
10660
+ if ((mistakes?.patterns.length ?? 0) > 0) {
10661
+ hardeningCandidates.push("Review latest compounding mistakes and convert one pattern into preventive continuity rule.");
10662
+ }
10663
+ const nextAction = hardeningCandidates[0] ?? "No critical drift detected; keep weekly/monthly continuity audit cadence.";
10664
+ const lines = [
10665
+ `# Continuity Audit \u2014 ${period} ${key}`,
10666
+ "",
10667
+ `Generated: ${nowIso}`,
10668
+ `Scope: ${period}`,
10669
+ "",
10670
+ "## Signal Summary",
10671
+ `- Identity anchor present: ${anchorPresent ? "yes" : "no"}`,
10672
+ `- Improvement-loop register present: ${improvementLoopPresent ? "yes" : "no"}`,
10673
+ `- Open incidents: ${openIncidents.length}`,
10674
+ `- Closed incidents: ${closedIncidents.length}`,
10675
+ `- Compounding mistake patterns: ${mistakes?.patterns.length ?? 0}`,
10676
+ "",
10677
+ "## Drift Checks",
10678
+ `- Identity anchor drift: ${anchorPresent ? "pass" : "needs attention"}`,
10679
+ `- Incident backlog: ${openIncidents.length === 0 ? "pass" : "needs attention"}`,
10680
+ `- Improvement-loop coverage: ${improvementLoopPresent ? "pass" : "needs attention"}`,
10681
+ "",
10682
+ "## Stale Rule Detection",
10683
+ `- Open incidents older than closure window: ${openIncidents.length > 0 ? "possible" : "none detected"}`,
10684
+ `- Preventive rule coverage on closed incidents: ${closedIncidents.some((i) => (i.preventiveRule ?? "").trim().length > 0) ? "present" : "not detected"}`,
10685
+ "",
10686
+ "## Next Hardening Action",
10687
+ `- ${nextAction}`,
10688
+ "",
10689
+ "## Open Incident IDs",
10690
+ ...openIncidents.length > 0 ? openIncidents.slice(0, 20).map((i) => `- ${i.id}`) : ["- (none)"],
10691
+ ""
10692
+ ];
10693
+ const dir = period === "weekly" ? this.identityAuditWeeklyDir : this.identityAuditMonthlyDir;
10694
+ await mkdir15(dir, { recursive: true });
10695
+ const reportPath = path21.join(dir, `${key}.md`);
10696
+ await writeFile14(reportPath, lines.join("\n"), "utf-8");
10697
+ return { period, key, reportPath };
10698
+ }
10609
10699
  async readMistakes() {
10610
10700
  try {
10611
10701
  const raw = await readFile15(this.mistakesPath, "utf-8");
@@ -10650,7 +10740,7 @@ var CompoundingEngine = class {
10650
10740
  const uniq = Array.from(new Set(patterns)).slice(0, 500);
10651
10741
  return { updatedAt: (/* @__PURE__ */ new Date()).toISOString(), patterns: uniq };
10652
10742
  }
10653
- formatWeeklyReport(weekId, entries, patterns) {
10743
+ formatWeeklyReport(weekId, entries, patterns, continuity) {
10654
10744
  const byAgent = /* @__PURE__ */ new Map();
10655
10745
  for (const e of entries) {
10656
10746
  const list = byAgent.get(e.agent) ?? [];
@@ -10689,8 +10779,61 @@ var CompoundingEngine = class {
10689
10779
  for (const p of patterns.slice(0, 100)) lines.push(`- ${p}`);
10690
10780
  }
10691
10781
  lines.push("");
10782
+ if (this.config.continuityAuditEnabled) {
10783
+ lines.push("## Continuity Audits");
10784
+ if (continuity.weeklyPath) {
10785
+ lines.push(`- weekly: ${continuity.weeklyPath}`);
10786
+ } else {
10787
+ lines.push(`- weekly: (missing for ${weekId})`);
10788
+ }
10789
+ if (continuity.monthlyPath) {
10790
+ lines.push(`- monthly: ${continuity.monthlyPath}`);
10791
+ } else {
10792
+ lines.push(`- monthly: (missing for ${continuity.monthId})`);
10793
+ }
10794
+ lines.push("");
10795
+ }
10692
10796
  return lines.join("\n");
10693
10797
  }
10798
+ async readNonEmptyFile(filePath) {
10799
+ try {
10800
+ const raw = await readFile15(filePath, "utf-8");
10801
+ return raw.trim().length > 0;
10802
+ } catch {
10803
+ return false;
10804
+ }
10805
+ }
10806
+ async readContinuityIncidents() {
10807
+ const incidents = [];
10808
+ try {
10809
+ const names = await readdir10(this.identityIncidentsDir);
10810
+ const files = names.filter((n) => n.endsWith(".md")).sort().reverse();
10811
+ for (const file of files) {
10812
+ const filePath = path21.join(this.identityIncidentsDir, file);
10813
+ try {
10814
+ const raw = await readFile15(filePath, "utf-8");
10815
+ const parsed = parseContinuityIncident(raw);
10816
+ if (parsed) incidents.push(parsed);
10817
+ } catch {
10818
+ }
10819
+ }
10820
+ } catch {
10821
+ }
10822
+ return incidents;
10823
+ }
10824
+ async readContinuityAuditReferences(weekId) {
10825
+ const monthId = monthIdFromIsoWeek(weekId);
10826
+ const weeklyPath = path21.join(this.identityAuditWeeklyDir, `${weekId}.md`);
10827
+ const monthlyPath = path21.join(this.identityAuditMonthlyDir, `${monthId}.md`);
10828
+ const weeklyExists = await this.readNonEmptyFile(weeklyPath);
10829
+ const monthlyExists = await this.readNonEmptyFile(monthlyPath);
10830
+ return {
10831
+ weekId,
10832
+ monthId,
10833
+ weeklyPath: weeklyExists ? weeklyPath : null,
10834
+ monthlyPath: monthlyExists ? monthlyPath : null
10835
+ };
10836
+ }
10694
10837
  };
10695
10838
 
10696
10839
  // src/orchestrator.ts
@@ -14011,6 +14154,57 @@ ${formatted}`
14011
14154
  },
14012
14155
  { name: "memory_search" }
14013
14156
  );
14157
+ api.registerTool(
14158
+ {
14159
+ name: "continuity_audit_generate",
14160
+ label: "Generate Continuity Audit",
14161
+ description: "Generate a deterministic identity continuity audit report (weekly/monthly) and persist it under identity/audits.",
14162
+ parameters: Type.Object({
14163
+ period: Type.Optional(
14164
+ Type.String({
14165
+ enum: ["weekly", "monthly"],
14166
+ description: "Audit period. Defaults to weekly."
14167
+ })
14168
+ ),
14169
+ key: Type.Optional(
14170
+ Type.String({
14171
+ description: "Optional period key (weekly: YYYY-Www, monthly: YYYY-MM). Defaults to current period."
14172
+ })
14173
+ )
14174
+ }),
14175
+ async execute(_toolCallId, params) {
14176
+ if (!orchestrator.config.identityContinuityEnabled) {
14177
+ return toolResult(
14178
+ "Identity continuity is disabled. Enable `identityContinuityEnabled: true` to generate continuity audits."
14179
+ );
14180
+ }
14181
+ if (!orchestrator.config.continuityAuditEnabled) {
14182
+ return toolResult(
14183
+ "Continuity audits are disabled. Enable `continuityAuditEnabled: true` to generate continuity audits."
14184
+ );
14185
+ }
14186
+ if (!orchestrator.compounding) {
14187
+ return toolResult(
14188
+ "Compounding engine is disabled. Enable `compoundingEnabled: true` to generate continuity audits."
14189
+ );
14190
+ }
14191
+ const period = params.period === "monthly" ? "monthly" : "weekly";
14192
+ const key = typeof params.key === "string" ? params.key : void 0;
14193
+ const audit = await orchestrator.compounding.synthesizeContinuityAudit({
14194
+ period,
14195
+ key
14196
+ });
14197
+ return toolResult(
14198
+ `OK
14199
+
14200
+ period: ${audit.period}
14201
+ key: ${audit.key}
14202
+ report: ${audit.reportPath}`
14203
+ );
14204
+ }
14205
+ },
14206
+ { name: "continuity_audit_generate" }
14207
+ );
14014
14208
  api.registerTool(
14015
14209
  {
14016
14210
  name: "continuity_incident_open",
@@ -15045,7 +15239,7 @@ mistakes: ${res.mistakesCount} patterns`
15045
15239
 
15046
15240
  // src/cli.ts
15047
15241
  import path33 from "path";
15048
- import { access as access2, readFile as readFile22, readdir as readdir12, unlink as unlink3 } from "fs/promises";
15242
+ import { access as access2, readFile as readFile22, readdir as readdir13, unlink as unlink3 } from "fs/promises";
15049
15243
 
15050
15244
  // src/transfer/export-json.ts
15051
15245
  import path25 from "path";
@@ -15057,7 +15251,7 @@ var EXPORT_SCHEMA_VERSION = 1;
15057
15251
 
15058
15252
  // src/transfer/fs-utils.ts
15059
15253
  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";
15254
+ import { mkdir as mkdir17, readdir as readdir11, readFile as readFile17, stat as stat3, writeFile as writeFile16 } from "fs/promises";
15061
15255
  import path24 from "path";
15062
15256
  async function sha256File(filePath) {
15063
15257
  const buf = await readFile17(filePath);
@@ -15080,7 +15274,7 @@ async function readJsonFile(filePath) {
15080
15274
  async function listFilesRecursive(rootDir) {
15081
15275
  const out = [];
15082
15276
  async function walk(dir) {
15083
- const entries = await readdir10(dir, { withFileTypes: true });
15277
+ const entries = await readdir11(dir, { withFileTypes: true });
15084
15278
  for (const ent of entries) {
15085
15279
  const fp = path24.join(dir, ent.name);
15086
15280
  if (ent.isDirectory()) {
@@ -15208,7 +15402,7 @@ async function looksLikeEngramMdExport(fromDir) {
15208
15402
 
15209
15403
  // src/transfer/backup.ts
15210
15404
  import path27 from "path";
15211
- import { mkdir as mkdir20, readdir as readdir11, rm as rm2 } from "fs/promises";
15405
+ import { mkdir as mkdir20, readdir as readdir12, rm as rm2 } from "fs/promises";
15212
15406
  function timestampDirName(now) {
15213
15407
  return now.toISOString().replace(/[:.]/g, "-");
15214
15408
  }
@@ -15229,7 +15423,7 @@ async function backupMemoryDir(opts) {
15229
15423
  return backupDir;
15230
15424
  }
15231
15425
  async function enforceRetention(outDirAbs, retentionDays) {
15232
- const entries = await readdir11(outDirAbs, { withFileTypes: true });
15426
+ const entries = await readdir12(outDirAbs, { withFileTypes: true });
15233
15427
  const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
15234
15428
  for (const ent of entries) {
15235
15429
  if (!ent.isDirectory()) continue;
@@ -15598,7 +15792,7 @@ async function readAllMemoryFiles(memoryDir) {
15598
15792
  const walk = async (dir) => {
15599
15793
  let entries;
15600
15794
  try {
15601
- entries = await readdir12(dir, { withFileTypes: true });
15795
+ entries = await readdir13(dir, { withFileTypes: true });
15602
15796
  } catch {
15603
15797
  return;
15604
15798
  }