@joshuaswarren/openclaw-engram 8.3.19 → 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
@@ -13914,6 +14057,22 @@ function mergeIdentityAnchor(existingRaw, updates) {
13914
14057
  }
13915
14058
  return lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
13916
14059
  }
14060
+ function formatContinuityIncidentSummary(incident, index) {
14061
+ const prefix = typeof index === "number" ? `### [${index + 1}] ` : "### ";
14062
+ const lines = [
14063
+ `${prefix}${incident.id} (${incident.state})`,
14064
+ `Opened: ${incident.openedAt}`
14065
+ ];
14066
+ if (incident.closedAt) lines.push(`Closed: ${incident.closedAt}`);
14067
+ if (incident.triggerWindow) lines.push(`Window: ${incident.triggerWindow}`);
14068
+ lines.push("", `Symptom: ${incident.symptom}`);
14069
+ if (incident.suspectedCause) lines.push(`Suspected Cause: ${incident.suspectedCause}`);
14070
+ if (incident.fixApplied) lines.push(`Fix Applied: ${incident.fixApplied}`);
14071
+ if (incident.verificationResult) lines.push(`Verification: ${incident.verificationResult}`);
14072
+ if (incident.preventiveRule) lines.push(`Preventive Rule: ${incident.preventiveRule}`);
14073
+ if (incident.filePath) lines.push(`Path: ${incident.filePath}`);
14074
+ return lines.join("\n");
14075
+ }
13917
14076
  function registerTools(api, orchestrator) {
13918
14077
  const actionTypes = [
13919
14078
  "store_episode",
@@ -13995,6 +14154,200 @@ ${formatted}`
13995
14154
  },
13996
14155
  { name: "memory_search" }
13997
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
+ );
14208
+ api.registerTool(
14209
+ {
14210
+ name: "continuity_incident_open",
14211
+ label: "Open Continuity Incident",
14212
+ description: "Create a new continuity incident record in append-only storage.",
14213
+ parameters: Type.Object({
14214
+ symptom: Type.String({
14215
+ description: "Observed continuity failure symptom."
14216
+ }),
14217
+ triggerWindow: Type.Optional(
14218
+ Type.String({
14219
+ description: "Optional time window when incident occurred."
14220
+ })
14221
+ ),
14222
+ suspectedCause: Type.Optional(
14223
+ Type.String({
14224
+ description: "Optional suspected root cause."
14225
+ })
14226
+ )
14227
+ }),
14228
+ async execute(_toolCallId, params) {
14229
+ if (!orchestrator.config.identityContinuityEnabled) {
14230
+ return toolResult(
14231
+ "Identity continuity is disabled. Enable `identityContinuityEnabled: true` to open incidents."
14232
+ );
14233
+ }
14234
+ if (!orchestrator.config.continuityIncidentLoggingEnabled) {
14235
+ return toolResult(
14236
+ "Continuity incident logging is disabled. Enable `continuityIncidentLoggingEnabled: true` to open incidents."
14237
+ );
14238
+ }
14239
+ const symptom = typeof params.symptom === "string" ? params.symptom.trim() : "";
14240
+ if (!symptom) {
14241
+ return toolResult("Missing required field: symptom");
14242
+ }
14243
+ const created = await orchestrator.storage.appendContinuityIncident({
14244
+ symptom,
14245
+ triggerWindow: typeof params.triggerWindow === "string" ? params.triggerWindow : void 0,
14246
+ suspectedCause: typeof params.suspectedCause === "string" ? params.suspectedCause : void 0
14247
+ });
14248
+ log.info(`continuity-incident open id=${created.id}`);
14249
+ return toolResult(`Continuity incident opened.
14250
+
14251
+ ${formatContinuityIncidentSummary(created)}`);
14252
+ }
14253
+ },
14254
+ { name: "continuity_incident_open" }
14255
+ );
14256
+ api.registerTool(
14257
+ {
14258
+ name: "continuity_incident_close",
14259
+ label: "Close Continuity Incident",
14260
+ description: "Close an open continuity incident with required verification details.",
14261
+ parameters: Type.Object({
14262
+ id: Type.String({
14263
+ description: "Incident ID to close."
14264
+ }),
14265
+ fixApplied: Type.String({
14266
+ description: "What fix was applied."
14267
+ }),
14268
+ verificationResult: Type.String({
14269
+ description: "How closure was verified."
14270
+ }),
14271
+ preventiveRule: Type.Optional(
14272
+ Type.String({
14273
+ description: "Optional preventive follow-up rule."
14274
+ })
14275
+ )
14276
+ }),
14277
+ async execute(_toolCallId, params) {
14278
+ if (!orchestrator.config.identityContinuityEnabled) {
14279
+ return toolResult(
14280
+ "Identity continuity is disabled. Enable `identityContinuityEnabled: true` to close incidents."
14281
+ );
14282
+ }
14283
+ if (!orchestrator.config.continuityIncidentLoggingEnabled) {
14284
+ return toolResult(
14285
+ "Continuity incident logging is disabled. Enable `continuityIncidentLoggingEnabled: true` to close incidents."
14286
+ );
14287
+ }
14288
+ const id = typeof params.id === "string" ? params.id.trim() : "";
14289
+ const fixApplied = typeof params.fixApplied === "string" ? params.fixApplied.trim() : "";
14290
+ const verificationResult = typeof params.verificationResult === "string" ? params.verificationResult.trim() : "";
14291
+ const preventiveRule = typeof params.preventiveRule === "string" ? params.preventiveRule.trim() : void 0;
14292
+ if (!id) return toolResult("Missing required field: id");
14293
+ if (!fixApplied) return toolResult("Missing required field: fixApplied");
14294
+ if (!verificationResult) return toolResult("Missing required field: verificationResult");
14295
+ const closed = await orchestrator.storage.closeContinuityIncident(id, {
14296
+ fixApplied,
14297
+ verificationResult,
14298
+ preventiveRule
14299
+ });
14300
+ if (!closed) return toolResult(`Incident not found: ${id}`);
14301
+ log.info(`continuity-incident close id=${id}`);
14302
+ return toolResult(`Continuity incident closed.
14303
+
14304
+ ${formatContinuityIncidentSummary(closed)}`);
14305
+ }
14306
+ },
14307
+ { name: "continuity_incident_close" }
14308
+ );
14309
+ api.registerTool(
14310
+ {
14311
+ name: "continuity_incident_list",
14312
+ label: "List Continuity Incidents",
14313
+ description: "List continuity incidents and optionally filter by state.",
14314
+ parameters: Type.Object({
14315
+ state: Type.Optional(
14316
+ Type.String({
14317
+ enum: ["open", "closed", "all"],
14318
+ description: "Incident state filter (default: open)."
14319
+ })
14320
+ ),
14321
+ limit: Type.Optional(
14322
+ Type.Number({
14323
+ description: "Max incidents to return (default: 25, max: 200).",
14324
+ minimum: 1,
14325
+ maximum: 200
14326
+ })
14327
+ )
14328
+ }),
14329
+ async execute(_toolCallId, params) {
14330
+ if (!orchestrator.config.identityContinuityEnabled) {
14331
+ return toolResult(
14332
+ "Identity continuity is disabled. Enable `identityContinuityEnabled: true` to list incidents."
14333
+ );
14334
+ }
14335
+ const state = params.state === "closed" || params.state === "all" ? params.state : "open";
14336
+ const limitRaw = typeof params.limit === "number" ? params.limit : 25;
14337
+ const limit = Math.max(1, Math.min(200, Math.floor(limitRaw)));
14338
+ const incidents = await orchestrator.storage.readContinuityIncidents(limit);
14339
+ const filtered = state === "all" ? incidents : incidents.filter((incident) => incident.state === state);
14340
+ if (filtered.length === 0) {
14341
+ return toolResult(`No continuity incidents found for state=${state}.`);
14342
+ }
14343
+ const body = filtered.map((incident, index) => formatContinuityIncidentSummary(incident, index)).join("\n\n");
14344
+ return toolResult(`## Continuity Incidents (${filtered.length}, state=${state})
14345
+
14346
+ ${body}`);
14347
+ }
14348
+ },
14349
+ { name: "continuity_incident_list" }
14350
+ );
13998
14351
  api.registerTool(
13999
14352
  {
14000
14353
  name: "identity_anchor_get",
@@ -14886,7 +15239,7 @@ mistakes: ${res.mistakesCount} patterns`
14886
15239
 
14887
15240
  // src/cli.ts
14888
15241
  import path33 from "path";
14889
- 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";
14890
15243
 
14891
15244
  // src/transfer/export-json.ts
14892
15245
  import path25 from "path";
@@ -14898,7 +15251,7 @@ var EXPORT_SCHEMA_VERSION = 1;
14898
15251
 
14899
15252
  // src/transfer/fs-utils.ts
14900
15253
  import { createHash as createHash6 } from "crypto";
14901
- 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";
14902
15255
  import path24 from "path";
14903
15256
  async function sha256File(filePath) {
14904
15257
  const buf = await readFile17(filePath);
@@ -14921,7 +15274,7 @@ async function readJsonFile(filePath) {
14921
15274
  async function listFilesRecursive(rootDir) {
14922
15275
  const out = [];
14923
15276
  async function walk(dir) {
14924
- const entries = await readdir10(dir, { withFileTypes: true });
15277
+ const entries = await readdir11(dir, { withFileTypes: true });
14925
15278
  for (const ent of entries) {
14926
15279
  const fp = path24.join(dir, ent.name);
14927
15280
  if (ent.isDirectory()) {
@@ -15049,7 +15402,7 @@ async function looksLikeEngramMdExport(fromDir) {
15049
15402
 
15050
15403
  // src/transfer/backup.ts
15051
15404
  import path27 from "path";
15052
- 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";
15053
15406
  function timestampDirName(now) {
15054
15407
  return now.toISOString().replace(/[:.]/g, "-");
15055
15408
  }
@@ -15070,7 +15423,7 @@ async function backupMemoryDir(opts) {
15070
15423
  return backupDir;
15071
15424
  }
15072
15425
  async function enforceRetention(outDirAbs, retentionDays) {
15073
- const entries = await readdir11(outDirAbs, { withFileTypes: true });
15426
+ const entries = await readdir12(outDirAbs, { withFileTypes: true });
15074
15427
  const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
15075
15428
  for (const ent of entries) {
15076
15429
  if (!ent.isDirectory()) continue;
@@ -15439,7 +15792,7 @@ async function readAllMemoryFiles(memoryDir) {
15439
15792
  const walk = async (dir) => {
15440
15793
  let entries;
15441
15794
  try {
15442
- entries = await readdir12(dir, { withFileTypes: true });
15795
+ entries = await readdir13(dir, { withFileTypes: true });
15443
15796
  } catch {
15444
15797
  return;
15445
15798
  }
@@ -15482,6 +15835,21 @@ async function readAllMemoryFiles(memoryDir) {
15482
15835
  }
15483
15836
  return out;
15484
15837
  }
15838
+ function formatContinuityIncidentCli(incident) {
15839
+ const lines = [
15840
+ `${incident.id} [${incident.state}]`,
15841
+ ` opened: ${incident.openedAt}`
15842
+ ];
15843
+ if (incident.closedAt) lines.push(` closed: ${incident.closedAt}`);
15844
+ if (incident.triggerWindow) lines.push(` window: ${incident.triggerWindow}`);
15845
+ lines.push(` symptom: ${incident.symptom}`);
15846
+ if (incident.suspectedCause) lines.push(` suspected-cause: ${incident.suspectedCause}`);
15847
+ if (incident.fixApplied) lines.push(` fix-applied: ${incident.fixApplied}`);
15848
+ if (incident.verificationResult) lines.push(` verification: ${incident.verificationResult}`);
15849
+ if (incident.preventiveRule) lines.push(` preventive-rule: ${incident.preventiveRule}`);
15850
+ if (incident.filePath) lines.push(` path: ${incident.filePath}`);
15851
+ return lines.join("\n");
15852
+ }
15485
15853
  function registerCli(api, orchestrator) {
15486
15854
  api.registerCli(
15487
15855
  ({ program }) => {
@@ -15868,6 +16236,90 @@ function registerCli(api, orchestrator) {
15868
16236
  }
15869
16237
  console.log(identity);
15870
16238
  });
16239
+ const continuityCmd = cmd.command("continuity").description("Identity continuity incident workflow commands");
16240
+ continuityCmd.command("incidents").description("List continuity incidents").option("--state <state>", "Filter by state: open|closed|all", "open").option("--limit <number>", "Maximum incidents to list", "25").action(async (...args) => {
16241
+ if (!orchestrator.config.identityContinuityEnabled) {
16242
+ console.log("Identity continuity is disabled.");
16243
+ return;
16244
+ }
16245
+ const options = args[0] ?? {};
16246
+ const stateRaw = String(options.state ?? "open").toLowerCase();
16247
+ const state = stateRaw === "closed" || stateRaw === "all" ? stateRaw : "open";
16248
+ const limit = Math.max(1, Math.min(200, parseInt(String(options.limit ?? "25"), 10) || 25));
16249
+ const incidents = await orchestrator.storage.readContinuityIncidents(limit);
16250
+ const filtered = state === "all" ? incidents : incidents.filter((incident) => incident.state === state);
16251
+ if (filtered.length === 0) {
16252
+ console.log(`No continuity incidents found for state=${state}.`);
16253
+ return;
16254
+ }
16255
+ console.log(`=== Continuity Incidents (${filtered.length}, state=${state}) ===
16256
+ `);
16257
+ for (const incident of filtered) {
16258
+ console.log(formatContinuityIncidentCli(incident));
16259
+ console.log();
16260
+ }
16261
+ });
16262
+ continuityCmd.command("incident-open").description("Open a continuity incident").option("--symptom <text>", "Required symptom description").option("--trigger-window <window>", "Optional incident trigger window").option("--suspected-cause <text>", "Optional suspected cause").action(async (...args) => {
16263
+ if (!orchestrator.config.identityContinuityEnabled) {
16264
+ console.log("Identity continuity is disabled.");
16265
+ return;
16266
+ }
16267
+ if (!orchestrator.config.continuityIncidentLoggingEnabled) {
16268
+ console.log("Continuity incident logging is disabled.");
16269
+ return;
16270
+ }
16271
+ const options = args[0] ?? {};
16272
+ const symptom = String(options.symptom ?? "").trim();
16273
+ if (!symptom) {
16274
+ console.log("Missing required --symptom.");
16275
+ return;
16276
+ }
16277
+ const created = await orchestrator.storage.appendContinuityIncident({
16278
+ symptom,
16279
+ triggerWindow: options.triggerWindow ? String(options.triggerWindow) : void 0,
16280
+ suspectedCause: options.suspectedCause ? String(options.suspectedCause) : void 0
16281
+ });
16282
+ console.log("Opened continuity incident:\n");
16283
+ console.log(formatContinuityIncidentCli(created));
16284
+ });
16285
+ continuityCmd.command("incident-close").description("Close a continuity incident").option("--id <id>", "Required incident ID").option("--fix-applied <text>", "Required fix description").option("--verification-result <text>", "Required verification result").option("--preventive-rule <text>", "Optional preventive rule").action(async (...args) => {
16286
+ if (!orchestrator.config.identityContinuityEnabled) {
16287
+ console.log("Identity continuity is disabled.");
16288
+ return;
16289
+ }
16290
+ if (!orchestrator.config.continuityIncidentLoggingEnabled) {
16291
+ console.log("Continuity incident logging is disabled.");
16292
+ return;
16293
+ }
16294
+ const options = args[0] ?? {};
16295
+ const id = String(options.id ?? "").trim();
16296
+ const fixApplied = String(options.fixApplied ?? "").trim();
16297
+ const verificationResult = String(options.verificationResult ?? "").trim();
16298
+ const preventiveRule = options.preventiveRule ? String(options.preventiveRule).trim() : void 0;
16299
+ if (!id) {
16300
+ console.log("Missing required --id.");
16301
+ return;
16302
+ }
16303
+ if (!fixApplied) {
16304
+ console.log("Missing required --fix-applied.");
16305
+ return;
16306
+ }
16307
+ if (!verificationResult) {
16308
+ console.log("Missing required --verification-result.");
16309
+ return;
16310
+ }
16311
+ const closed = await orchestrator.storage.closeContinuityIncident(id, {
16312
+ fixApplied,
16313
+ verificationResult,
16314
+ preventiveRule
16315
+ });
16316
+ if (!closed) {
16317
+ console.log(`Incident not found: ${id}`);
16318
+ return;
16319
+ }
16320
+ console.log("Closed continuity incident:\n");
16321
+ console.log(formatContinuityIncidentCli(closed));
16322
+ });
15871
16323
  cmd.command("access").description("Show memory access statistics").option("-n, --top <number>", "Show top N most accessed", "20").action(async (...args) => {
15872
16324
  const options = args[0] ?? {};
15873
16325
  const top = parseInt(options.top ?? "20", 10);