@joshuaswarren/openclaw-engram 8.3.17 → 8.3.18

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
@@ -4317,6 +4317,104 @@ function confidenceTier(score) {
4317
4317
  }
4318
4318
  var SPECULATIVE_TTL_DAYS = 30;
4319
4319
 
4320
+ // src/identity-continuity.ts
4321
+ function parseFrontmatterValue(raw) {
4322
+ try {
4323
+ return JSON.parse(raw);
4324
+ } catch {
4325
+ return raw;
4326
+ }
4327
+ }
4328
+ function parseFrontmatter(raw) {
4329
+ const parsed = {};
4330
+ for (const line of raw.split("\n")) {
4331
+ const idx = line.indexOf(":");
4332
+ if (idx <= 0) continue;
4333
+ const key = line.slice(0, idx).trim();
4334
+ const value = line.slice(idx + 1).trim();
4335
+ parsed[key] = parseFrontmatterValue(value);
4336
+ }
4337
+ return parsed;
4338
+ }
4339
+ function emitSection(lines, title, value) {
4340
+ if (!value || value.trim().length === 0) return;
4341
+ lines.push(`## ${title}`, "", value.trim(), "");
4342
+ }
4343
+ function parseSection(body, title) {
4344
+ const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4345
+ const re = new RegExp(`## ${escaped}\\n\\n([\\s\\S]*?)(?=\\n## |$)`);
4346
+ const match = body.match(re);
4347
+ if (!match) return void 0;
4348
+ const value = match[1].trim();
4349
+ return value.length > 0 ? value : void 0;
4350
+ }
4351
+ function serializeContinuityIncident(incident) {
4352
+ const lines = [
4353
+ "---",
4354
+ `id: ${JSON.stringify(incident.id)}`,
4355
+ `state: ${JSON.stringify(incident.state)}`,
4356
+ `openedAt: ${JSON.stringify(incident.openedAt)}`,
4357
+ `updatedAt: ${JSON.stringify(incident.updatedAt)}`
4358
+ ];
4359
+ if (incident.closedAt) lines.push(`closedAt: ${JSON.stringify(incident.closedAt)}`);
4360
+ if (incident.triggerWindow) lines.push(`triggerWindow: ${JSON.stringify(incident.triggerWindow)}`);
4361
+ lines.push("---", "");
4362
+ emitSection(lines, "Symptom", incident.symptom);
4363
+ emitSection(lines, "Suspected Cause", incident.suspectedCause);
4364
+ emitSection(lines, "Fix Applied", incident.fixApplied);
4365
+ emitSection(lines, "Verification Result", incident.verificationResult);
4366
+ emitSection(lines, "Preventive Rule", incident.preventiveRule);
4367
+ return lines.join("\n").trimEnd() + "\n";
4368
+ }
4369
+ function parseContinuityIncident(raw) {
4370
+ const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
4371
+ if (!match) return null;
4372
+ const frontmatter = parseFrontmatter(match[1]);
4373
+ const body = match[2] ?? "";
4374
+ const id = typeof frontmatter.id === "string" ? frontmatter.id : "";
4375
+ const stateRaw = frontmatter.state;
4376
+ const state = stateRaw === "closed" ? "closed" : stateRaw === "open" ? "open" : "open";
4377
+ const openedAt = typeof frontmatter.openedAt === "string" ? frontmatter.openedAt : "";
4378
+ const updatedAt = typeof frontmatter.updatedAt === "string" ? frontmatter.updatedAt : openedAt;
4379
+ const symptom = parseSection(body, "Symptom");
4380
+ if (!id || !openedAt || !updatedAt || !symptom) return null;
4381
+ return {
4382
+ id,
4383
+ state,
4384
+ openedAt,
4385
+ updatedAt,
4386
+ triggerWindow: typeof frontmatter.triggerWindow === "string" ? frontmatter.triggerWindow : void 0,
4387
+ symptom,
4388
+ suspectedCause: parseSection(body, "Suspected Cause"),
4389
+ fixApplied: parseSection(body, "Fix Applied"),
4390
+ verificationResult: parseSection(body, "Verification Result"),
4391
+ preventiveRule: parseSection(body, "Preventive Rule"),
4392
+ closedAt: typeof frontmatter.closedAt === "string" ? frontmatter.closedAt : void 0
4393
+ };
4394
+ }
4395
+ function createContinuityIncidentRecord(id, input, nowIso) {
4396
+ return {
4397
+ id,
4398
+ state: "open",
4399
+ openedAt: nowIso,
4400
+ updatedAt: nowIso,
4401
+ triggerWindow: input.triggerWindow?.trim() || void 0,
4402
+ symptom: input.symptom.trim(),
4403
+ suspectedCause: input.suspectedCause?.trim() || void 0
4404
+ };
4405
+ }
4406
+ function closeContinuityIncidentRecord(incident, closure, nowIso) {
4407
+ return {
4408
+ ...incident,
4409
+ state: "closed",
4410
+ updatedAt: nowIso,
4411
+ closedAt: nowIso,
4412
+ fixApplied: closure.fixApplied.trim(),
4413
+ verificationResult: closure.verificationResult.trim(),
4414
+ preventiveRule: closure.preventiveRule?.trim() || incident.preventiveRule
4415
+ };
4416
+ }
4417
+
4320
4418
  // src/storage.ts
4321
4419
  var ARTIFACT_SEARCH_STOPWORDS = /* @__PURE__ */ new Set([
4322
4420
  "a",
@@ -4416,7 +4514,7 @@ function serializeFrontmatter(fm) {
4416
4514
  lines.push("---");
4417
4515
  return lines.join("\n");
4418
4516
  }
4419
- function parseFrontmatter(raw) {
4517
+ function parseFrontmatter2(raw) {
4420
4518
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
4421
4519
  if (!match) return null;
4422
4520
  const fmBlock = match[1];
@@ -4804,6 +4902,24 @@ var StorageManager = class _StorageManager {
4804
4902
  get artifactsDir() {
4805
4903
  return path4.join(this.baseDir, "artifacts");
4806
4904
  }
4905
+ get identityDir() {
4906
+ return path4.join(this.baseDir, "identity");
4907
+ }
4908
+ get identityAnchorPath() {
4909
+ return path4.join(this.identityDir, "identity-anchor.md");
4910
+ }
4911
+ get identityIncidentsDir() {
4912
+ return path4.join(this.identityDir, "incidents");
4913
+ }
4914
+ get identityAuditsWeeklyDir() {
4915
+ return path4.join(this.identityDir, "audits", "weekly");
4916
+ }
4917
+ get identityAuditsMonthlyDir() {
4918
+ return path4.join(this.identityDir, "audits", "monthly");
4919
+ }
4920
+ get identityImprovementLoopsPath() {
4921
+ return path4.join(this.identityDir, "improvement-loops.md");
4922
+ }
4807
4923
  get profilePath() {
4808
4924
  return path4.join(this.baseDir, "profile.md");
4809
4925
  }
@@ -4839,6 +4955,10 @@ var StorageManager = class _StorageManager {
4839
4955
  await mkdir2(this.stateDir, { recursive: true });
4840
4956
  await mkdir2(this.questionsDir, { recursive: true });
4841
4957
  await mkdir2(this.artifactsDir, { recursive: true });
4958
+ await mkdir2(this.identityDir, { recursive: true });
4959
+ await mkdir2(this.identityIncidentsDir, { recursive: true });
4960
+ await mkdir2(this.identityAuditsWeeklyDir, { recursive: true });
4961
+ await mkdir2(this.identityAuditsMonthlyDir, { recursive: true });
4842
4962
  await mkdir2(path4.join(this.baseDir, "config"), { recursive: true });
4843
4963
  }
4844
4964
  async writeMemory(category, content, options = {}) {
@@ -5114,7 +5234,7 @@ ${sanitized.text}
5114
5234
  } else if (entry.name.endsWith(".md")) {
5115
5235
  try {
5116
5236
  const raw = await readFile2(fullPath, "utf-8");
5117
- const parsed = parseFrontmatter(raw);
5237
+ const parsed = parseFrontmatter2(raw);
5118
5238
  if (parsed) {
5119
5239
  memories.push({
5120
5240
  path: fullPath,
@@ -5150,7 +5270,7 @@ ${sanitized.text}
5150
5270
  } else if (entry.name.endsWith(".md")) {
5151
5271
  try {
5152
5272
  const raw = await readFile2(fullPath, "utf-8");
5153
- const parsed = parseFrontmatter(raw);
5273
+ const parsed = parseFrontmatter2(raw);
5154
5274
  if (parsed) {
5155
5275
  memories.push({
5156
5276
  path: fullPath,
@@ -5172,7 +5292,7 @@ ${sanitized.text}
5172
5292
  async readMemoryByPath(filePath) {
5173
5293
  try {
5174
5294
  const raw = await readFile2(filePath, "utf-8");
5175
- const parsed = parseFrontmatter(raw);
5295
+ const parsed = parseFrontmatter2(raw);
5176
5296
  if (!parsed) return null;
5177
5297
  return { path: filePath, frontmatter: parsed.frontmatter, content: parsed.content };
5178
5298
  } catch {
@@ -5448,6 +5568,86 @@ ${memory.content}
5448
5568
  return null;
5449
5569
  }
5450
5570
  }
5571
+ async writeIdentityAnchor(content) {
5572
+ await this.ensureDirectories();
5573
+ await writeFile2(this.identityAnchorPath, content, "utf-8");
5574
+ }
5575
+ async readIdentityAnchor() {
5576
+ try {
5577
+ return await readFile2(this.identityAnchorPath, "utf-8");
5578
+ } catch {
5579
+ return null;
5580
+ }
5581
+ }
5582
+ async appendContinuityIncident(input) {
5583
+ await this.ensureDirectories();
5584
+ const now = /* @__PURE__ */ new Date();
5585
+ const nowIso = now.toISOString();
5586
+ const date = nowIso.slice(0, 10);
5587
+ const id = this.generateId("incident");
5588
+ const incident = createContinuityIncidentRecord(id, input, nowIso);
5589
+ const filePath = path4.join(this.identityIncidentsDir, `${date}-${id}.md`);
5590
+ await writeFile2(filePath, serializeContinuityIncident(incident), "utf-8");
5591
+ return { ...incident, filePath };
5592
+ }
5593
+ async readContinuityIncidents(limit = 200) {
5594
+ const cappedLimit = Math.max(0, Math.floor(limit));
5595
+ if (cappedLimit === 0) return [];
5596
+ try {
5597
+ const candidates = await this.readContinuityIncidentFileNames();
5598
+ const incidents = [];
5599
+ for (const file of candidates) {
5600
+ if (incidents.length >= cappedLimit) break;
5601
+ const filePath = path4.join(this.identityIncidentsDir, file);
5602
+ try {
5603
+ const raw = await readFile2(filePath, "utf-8");
5604
+ const parsed = parseContinuityIncident(raw);
5605
+ if (parsed) incidents.push({ ...parsed, filePath });
5606
+ } catch {
5607
+ }
5608
+ }
5609
+ return incidents;
5610
+ } catch {
5611
+ return [];
5612
+ }
5613
+ }
5614
+ async closeContinuityIncident(id, closure) {
5615
+ const directFilePath = await this.findContinuityIncidentFilePathById(id);
5616
+ const target = directFilePath ? await this.readContinuityIncidentFile(directFilePath) : null;
5617
+ if (!target || !directFilePath) return null;
5618
+ if (target.state === "closed") return target;
5619
+ const closed = closeContinuityIncidentRecord(target, closure, (/* @__PURE__ */ new Date()).toISOString());
5620
+ await writeFile2(directFilePath, serializeContinuityIncident(closed), "utf-8");
5621
+ return { ...closed, filePath: directFilePath };
5622
+ }
5623
+ async writeIdentityAudit(period, key, content) {
5624
+ await this.ensureDirectories();
5625
+ const safeKey = this.sanitizeIdentityAuditKey(key);
5626
+ const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
5627
+ const filePath = path4.join(dir, `${safeKey}.md`);
5628
+ await writeFile2(filePath, content, "utf-8");
5629
+ return filePath;
5630
+ }
5631
+ async readIdentityAudit(period, key) {
5632
+ try {
5633
+ const safeKey = this.sanitizeIdentityAuditKey(key);
5634
+ const dir = period === "weekly" ? this.identityAuditsWeeklyDir : this.identityAuditsMonthlyDir;
5635
+ return await readFile2(path4.join(dir, `${safeKey}.md`), "utf-8");
5636
+ } catch {
5637
+ return null;
5638
+ }
5639
+ }
5640
+ async writeIdentityImprovementLoops(content) {
5641
+ await this.ensureDirectories();
5642
+ await writeFile2(this.identityImprovementLoopsPath, content, "utf-8");
5643
+ }
5644
+ async readIdentityImprovementLoops() {
5645
+ try {
5646
+ return await readFile2(this.identityImprovementLoopsPath, "utf-8");
5647
+ } catch {
5648
+ return null;
5649
+ }
5650
+ }
5451
5651
  // ---------------------------------------------------------------------------
5452
5652
  // Question storage
5453
5653
  // ---------------------------------------------------------------------------
@@ -5456,6 +5656,37 @@ ${memory.content}
5456
5656
  const rand = Math.random().toString(36).slice(2, 4);
5457
5657
  return `${prefix}-${ts}-${rand}`;
5458
5658
  }
5659
+ async readContinuityIncidentFileNames() {
5660
+ const files = await readdir(this.identityIncidentsDir);
5661
+ return files.filter((file) => file.endsWith(".md")).sort().reverse();
5662
+ }
5663
+ async readContinuityIncidentFile(filePath) {
5664
+ try {
5665
+ const raw = await readFile2(filePath, "utf-8");
5666
+ const parsed = parseContinuityIncident(raw);
5667
+ return parsed ? { ...parsed, filePath } : null;
5668
+ } catch {
5669
+ return null;
5670
+ }
5671
+ }
5672
+ async findContinuityIncidentFilePathById(id) {
5673
+ const fileNames = await this.readContinuityIncidentFileNames();
5674
+ const directMatch = fileNames.find((name) => name.endsWith(`-${id}.md`));
5675
+ if (directMatch) return path4.join(this.identityIncidentsDir, directMatch);
5676
+ for (const fileName of fileNames) {
5677
+ const filePath = path4.join(this.identityIncidentsDir, fileName);
5678
+ const parsed = await this.readContinuityIncidentFile(filePath);
5679
+ if (parsed?.id === id) return filePath;
5680
+ }
5681
+ return null;
5682
+ }
5683
+ sanitizeIdentityAuditKey(key) {
5684
+ const trimmed = key.trim();
5685
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(trimmed) || trimmed.includes("..")) {
5686
+ throw new Error("Invalid identity audit key");
5687
+ }
5688
+ return trimmed;
5689
+ }
5459
5690
  async writeQuestion(question, context, priority) {
5460
5691
  await mkdir2(this.questionsDir, { recursive: true });
5461
5692
  const id = this.generateId("q");