@joshuaswarren/openclaw-engram 9.0.9 → 9.0.11

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
@@ -474,6 +474,8 @@ function parseConfig(raw) {
474
474
  recallConfidenceGateEnabled: cfg.recallConfidenceGateEnabled === true,
475
475
  recallConfidenceGateThreshold: typeof cfg.recallConfidenceGateThreshold === "number" ? Math.max(0, Math.min(1, cfg.recallConfidenceGateThreshold)) : 0.12,
476
476
  causalRuleExtractionEnabled: cfg.causalRuleExtractionEnabled === true,
477
+ memoryReconstructionEnabled: cfg.memoryReconstructionEnabled === true,
478
+ memoryReconstructionMaxExpansions: typeof cfg.memoryReconstructionMaxExpansions === "number" ? Math.max(0, Math.round(cfg.memoryReconstructionMaxExpansions)) : 3,
477
479
  graphLateralInhibitionEnabled: cfg.graphLateralInhibitionEnabled !== false,
478
480
  graphLateralInhibitionBeta: typeof cfg.graphLateralInhibitionBeta === "number" ? Math.max(0, Math.min(1, cfg.graphLateralInhibitionBeta)) : 0.15,
479
481
  graphLateralInhibitionTopM: typeof cfg.graphLateralInhibitionTopM === "number" ? Math.max(0, Math.round(cfg.graphLateralInhibitionTopM)) : 7,
@@ -4179,6 +4181,26 @@ function rescoreMemoryImportance(memory) {
4179
4181
  return scoreImportance(memory.content, memory.frontmatter.category, memory.frontmatter.tags ?? []);
4180
4182
  }
4181
4183
 
4184
+ // src/reconstruct.ts
4185
+ function findUnresolvedEntityRefs(recalledSnippets, recalledEntityRefs, knownEntities) {
4186
+ const refSet = new Set(recalledEntityRefs.map((r) => r.toLowerCase()));
4187
+ const combinedText = recalledSnippets.join(" ").toLowerCase();
4188
+ const sorted = [...knownEntities].sort((a, b) => b.length - a.length);
4189
+ const matched = /* @__PURE__ */ new Set();
4190
+ const unresolved = [];
4191
+ for (const entity of sorted) {
4192
+ const lower = entity.toLowerCase();
4193
+ if (refSet.has(lower)) continue;
4194
+ const escaped = lower.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4195
+ const pattern = new RegExp(`(?:^|[\\s,;:'"(\\[])${escaped}(?:$|[\\s,;:'".)\\]])`, "i");
4196
+ if (pattern.test(combinedText) && !matched.has(lower)) {
4197
+ unresolved.push(entity);
4198
+ matched.add(lower);
4199
+ }
4200
+ }
4201
+ return unresolved;
4202
+ }
4203
+
4182
4204
  // src/search/noop-backend.ts
4183
4205
  var NoopSearchBackend = class {
4184
4206
  async probe() {
@@ -12536,6 +12558,9 @@ function serializeBoxFrontmatter(fm) {
12536
12558
  ];
12537
12559
  if (fm.sessionKey) lines.push(`sessionKey: ${fm.sessionKey}`);
12538
12560
  if (fm.traceId) lines.push(`traceId: ${fm.traceId}`);
12561
+ if (fm.goal) lines.push(`goal: ${fm.goal.replace(/[\r\n]+/g, " ")}`);
12562
+ if (fm.toolsUsed?.length) lines.push(`toolsUsed: [${fm.toolsUsed.map((t) => `"${t}"`).join(", ")}]`);
12563
+ if (fm.outcome) lines.push(`outcome: ${fm.outcome}`);
12539
12564
  lines.push("---");
12540
12565
  return lines.join("\n");
12541
12566
  }
@@ -12555,6 +12580,7 @@ function parseBoxFrontmatter(raw) {
12555
12580
  if (!m) return [];
12556
12581
  return m[1].split(",").map((s) => s.trim().replace(/^"|"$/g, "")).filter(Boolean);
12557
12582
  };
12583
+ const outcome = fm.outcome;
12558
12584
  return {
12559
12585
  id: fm.id ?? "",
12560
12586
  memoryKind: "box",
@@ -12564,7 +12590,10 @@ function parseBoxFrontmatter(raw) {
12564
12590
  sessionKey: fm.sessionKey,
12565
12591
  topics: parseArray(fm.topics),
12566
12592
  memoryIds: parseArray(fm.memoryIds),
12567
- traceId: fm.traceId
12593
+ traceId: fm.traceId,
12594
+ goal: fm.goal || void 0,
12595
+ toolsUsed: fm.toolsUsed ? parseArray(fm.toolsUsed) : void 0,
12596
+ outcome: outcome && ["success", "failure", "partial", "unknown"].includes(outcome) ? outcome : void 0
12568
12597
  };
12569
12598
  }
12570
12599
  var BoxBuilder = class {
@@ -12650,6 +12679,10 @@ var BoxBuilder = class {
12650
12679
  const topicSet = /* @__PURE__ */ new Set([...this.openBox.topics, ...newTopics]);
12651
12680
  this.openBox.topics = [...topicSet];
12652
12681
  this.openBox.memoryIds.push(...event.memoryIds);
12682
+ if (event.toolsUsed?.length) {
12683
+ const toolSet = /* @__PURE__ */ new Set([...this.openBox.toolsUsed ?? [], ...event.toolsUsed]);
12684
+ this.openBox.toolsUsed = [...toolSet];
12685
+ }
12653
12686
  await this.sealCurrent("max_memories");
12654
12687
  } else if (topicShifted) {
12655
12688
  await this.sealCurrent("topic_shift");
@@ -12664,6 +12697,10 @@ var BoxBuilder = class {
12664
12697
  const topicSet = /* @__PURE__ */ new Set([...this.openBox.topics, ...newTopics]);
12665
12698
  this.openBox.topics = [...topicSet];
12666
12699
  this.openBox.lastActivityAt = now.toISOString();
12700
+ if (event.toolsUsed?.length) {
12701
+ const toolSet = /* @__PURE__ */ new Set([...this.openBox.toolsUsed ?? [], ...event.toolsUsed]);
12702
+ this.openBox.toolsUsed = [...toolSet];
12703
+ }
12667
12704
  await this.saveOpenBox();
12668
12705
  }
12669
12706
  } else {
@@ -12681,7 +12718,9 @@ var BoxBuilder = class {
12681
12718
  createdAt: ts,
12682
12719
  lastActivityAt: ts,
12683
12720
  topics: event.topics.filter(Boolean),
12684
- memoryIds: [...event.memoryIds]
12721
+ memoryIds: [...event.memoryIds],
12722
+ goal: event.goal,
12723
+ toolsUsed: event.toolsUsed?.length ? [...event.toolsUsed] : void 0
12685
12724
  };
12686
12725
  }
12687
12726
  /**
@@ -12713,7 +12752,10 @@ var BoxBuilder = class {
12713
12752
  sealReason: reason,
12714
12753
  topics: box.topics,
12715
12754
  memoryIds: box.memoryIds,
12716
- traceId
12755
+ traceId,
12756
+ goal: box.goal,
12757
+ toolsUsed: box.toolsUsed?.length ? box.toolsUsed : void 0,
12758
+ outcome: "unknown"
12717
12759
  };
12718
12760
  const content = `${serializeBoxFrontmatter(fm)}
12719
12761
 
@@ -17842,6 +17884,37 @@ ${tmtNode.summary}`);
17842
17884
  confidenceGateRejected = true;
17843
17885
  }
17844
17886
  memoryResults = memoryResults.slice(0, recallResultLimit);
17887
+ if (this.config.memoryReconstructionEnabled && memoryResults.length > 0) {
17888
+ try {
17889
+ const snippets = memoryResults.map((r) => r.snippet);
17890
+ const coveredRefs = memoryResults.map((r) => r.path).filter((p) => p.startsWith("entities/")).map((p) => p.replace(/^entities\//, "").replace(/\.md$/, ""));
17891
+ const knownEntities = await profileStorage.listEntityNames();
17892
+ const missing = findUnresolvedEntityRefs(snippets, coveredRefs, knownEntities);
17893
+ if (missing.length > 0) {
17894
+ const budget = this.config.memoryReconstructionMaxExpansions;
17895
+ let expanded = 0;
17896
+ for (const entityName of missing) {
17897
+ if (expanded >= budget) break;
17898
+ const raw = await profileStorage.readEntity(entityName);
17899
+ if (raw && raw.length > 0) {
17900
+ const snippet = raw.length > 300 ? raw.slice(0, 300) + "\u2026" : raw;
17901
+ memoryResults.push({
17902
+ docid: `entity:${entityName}`,
17903
+ path: `entities/${entityName}.md`,
17904
+ snippet: `[Entity: ${entityName}] ${snippet}`,
17905
+ score: 0.1
17906
+ });
17907
+ expanded++;
17908
+ }
17909
+ }
17910
+ if (expanded > 0) {
17911
+ log.debug(`recall: reconstructed ${expanded} entity contexts`);
17912
+ }
17913
+ }
17914
+ } catch (err) {
17915
+ log.warn("recall: memory reconstruction failed (non-fatal)", err);
17916
+ }
17917
+ }
17845
17918
  if (memoryResults.length > 0) {
17846
17919
  recallSource = "hot_qmd";
17847
17920
  recalledMemoryCount = memoryResults.length;
@@ -18387,10 +18460,13 @@ _Context: ${topQuestion.context}_`
18387
18460
  await clearBuffer();
18388
18461
  if (this.config.memoryBoxesEnabled && persistedIds.length > 0) {
18389
18462
  const extractionTopics = deriveTopicsFromExtraction(result);
18463
+ const firstUserTurn = turns.find((t) => t.role === "user");
18464
+ const boxGoal = firstUserTurn?.content?.slice(0, 100)?.trim() || void 0;
18390
18465
  await this.boxBuilderFor(storage).onExtraction({
18391
18466
  topics: extractionTopics,
18392
18467
  memoryIds: persistedIds,
18393
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
18468
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
18469
+ goal: boxGoal
18394
18470
  }).catch((err) => log.warn("[boxes] onExtraction failed (non-fatal)", err));
18395
18471
  }
18396
18472
  if (this.config.threadingEnabled && threadIdForExtraction && persistedIds.length > 0) {