@joshuaswarren/openclaw-engram 9.0.47 → 9.0.48

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/README.md CHANGED
@@ -47,6 +47,7 @@ AI agents forget everything between conversations. Engram fixes that.
47
47
  - **Artifact recovery recall** — Engram can now, when both `creationMemoryEnabled` and `workProductRecallEnabled` are enabled, surface prompt-relevant work-product ledger entries back into recall as a dedicated `Work Products` section and inspect reuse candidates with `openclaw engram work-product-recall-search`.
48
48
  - **Commitment lifecycle foundation** — Engram can now, when `creationMemoryEnabled`, `commitmentLedgerEnabled`, and `commitmentLifecycleEnabled` are enabled, transition existing commitments to `fulfilled` / `cancelled` / `expired`, inspect overdue and stale obligations in `openclaw engram commitment-status`, and run deterministic lifecycle cleanup with `openclaw engram commitment-lifecycle-run`.
49
49
  - **Resume-bundle builder** — Engram can now, when `creationMemoryEnabled` and `resumeBundlesEnabled` are enabled, persist typed crash-recovery resume bundles, inspect them with `openclaw engram resume-bundle-status`, write explicit handoff shells through `openclaw engram resume-bundle-record`, and build bounded resume bundles from transcript recovery, recent objective-state snapshots, work products, and open commitments through `openclaw engram resume-bundle-build`.
50
+ - **Utility-learning telemetry foundation** — Engram can now, when `memoryUtilityLearningEnabled` is enabled, persist typed downstream utility events for promotion and ranking decisions, inspect them with `openclaw engram utility-status`, and write deterministic benchmark/operator telemetry through `openclaw engram utility-record` before learned weighting lands.
50
51
  - **Zero-config start** — Install, add an API key, restart. Engram works out of the box with sensible defaults and progressively unlocks advanced features as you enable them.
51
52
 
52
53
  ## Quick Start
@@ -183,6 +184,8 @@ openclaw engram commitment-lifecycle-run # Expire overdue commitments and clean
183
184
  openclaw engram work-product-status # Work-product ledger counts and latest recorded output
184
185
  openclaw engram work-product-record # Record a typed work-product ledger entry
185
186
  openclaw engram work-product-recall-search <query> # Preview reusable work products from the creation-memory ledger
187
+ openclaw engram utility-status # Utility-learning telemetry counts and latest observed outcome event
188
+ openclaw engram utility-record # Record a typed utility-learning telemetry event
186
189
  openclaw engram conversation-index-health # Conversation index status
187
190
  openclaw engram graph-health # Entity graph status
188
191
  openclaw engram tier-status # Hot/cold tier metrics
@@ -225,6 +228,8 @@ Key settings:
225
228
  | `semanticRulePromotionEnabled` | `false` | Enable deterministic promotion of explicit `IF ... THEN ...` rules from verified episodic memories via `openclaw engram semantic-rule-promote` |
226
229
  | `semanticRuleVerificationEnabled` | `false` | Verify promoted semantic rules against their cited source episodes at recall time and inject a dedicated `Verified Rules` section via `openclaw engram semantic-rule-verify` |
227
230
  | `creationMemoryEnabled` | `false` | Enable the creation-memory foundation, including the work-product ledger and operator-facing write/status commands |
231
+ | `memoryUtilityLearningEnabled` | `false` | Enable typed utility-learning telemetry storage for future promotion/ranking learners |
232
+ | `promotionByOutcomeEnabled` | `false` | Reserve outcome-aware promotion controls that later slices will drive from learned utility signals |
228
233
  | `commitmentLedgerEnabled` | `false` | Enable the explicit commitment ledger for promises, follow-ups, deadlines, and unfinished obligations |
229
234
  | `commitmentLifecycleEnabled` | `false` | Enable commitment lifecycle transitions, stale tracking, and resolved-entry cleanup for the commitment ledger |
230
235
  | `commitmentStaleDays` | `14` | Days before an open commitment without a due date is considered stale in lifecycle status |
package/dist/index.js CHANGED
@@ -308,6 +308,8 @@ function parseConfig(raw) {
308
308
  semanticRulePromotionEnabled: cfg.semanticRulePromotionEnabled === true,
309
309
  semanticRuleVerificationEnabled: cfg.semanticRuleVerificationEnabled === true,
310
310
  creationMemoryEnabled: cfg.creationMemoryEnabled === true,
311
+ memoryUtilityLearningEnabled: cfg.memoryUtilityLearningEnabled === true,
312
+ promotionByOutcomeEnabled: cfg.promotionByOutcomeEnabled === true,
311
313
  commitmentLedgerEnabled: cfg.commitmentLedgerEnabled === true,
312
314
  commitmentLifecycleEnabled: cfg.commitmentLifecycleEnabled === true,
313
315
  commitmentStaleDays: typeof cfg.commitmentStaleDays === "number" ? cfg.commitmentStaleDays : 14,
@@ -25309,7 +25311,7 @@ promotionCandidates: ${res.promotionCandidateCount}`
25309
25311
  }
25310
25312
 
25311
25313
  // src/cli.ts
25312
- import path60 from "path";
25314
+ import path61 from "path";
25313
25315
  import { access as access3, readFile as readFile37, readdir as readdir24, unlink as unlink8 } from "fs/promises";
25314
25316
  import { createHash as createHash10 } from "crypto";
25315
25317
 
@@ -26190,8 +26192,8 @@ function gatherCandidates(input, warnings) {
26190
26192
  const record = rec;
26191
26193
  const content = typeof record.content === "string" ? record.content : null;
26192
26194
  if (!content) continue;
26193
- const path62 = typeof record.path === "string" ? record.path : "";
26194
- if (!path62.startsWith("transcripts/") && !path62.includes("/transcripts/")) continue;
26195
+ const path63 = typeof record.path === "string" ? record.path : "";
26196
+ if (!path63.startsWith("transcripts/") && !path63.includes("/transcripts/")) continue;
26195
26197
  rows.push(...parseJsonl(content, warnings));
26196
26198
  }
26197
26199
  return rows;
@@ -27825,15 +27827,147 @@ async function runCompatChecks(options) {
27825
27827
  };
27826
27828
  }
27827
27829
 
27828
- // src/resume-bundles.ts
27830
+ // src/utility-telemetry.ts
27829
27831
  import path59 from "path";
27830
27832
  import { mkdir as mkdir40, writeFile as writeFile37 } from "fs/promises";
27833
+ function resolveUtilityTelemetryDir(memoryDir, overrideDir) {
27834
+ if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
27835
+ return overrideDir.trim();
27836
+ }
27837
+ return path59.join(memoryDir, "state", "utility-telemetry");
27838
+ }
27839
+ function assertUtilityScore(value) {
27840
+ if (typeof value !== "number" || !Number.isFinite(value)) {
27841
+ throw new Error("utilityScore must be a finite number");
27842
+ }
27843
+ if (value < -1 || value > 1) {
27844
+ throw new Error("utilityScore must be between -1 and 1");
27845
+ }
27846
+ return value;
27847
+ }
27848
+ function validateUtilityTelemetryEvent(raw) {
27849
+ if (!isRecord2(raw)) throw new Error("utility telemetry event must be an object");
27850
+ if (raw.schemaVersion !== 1) throw new Error("schemaVersion must be 1");
27851
+ const source = assertString2(raw.source, "source");
27852
+ if (!["cli", "system", "benchmark", "tool_result"].includes(source)) {
27853
+ throw new Error("source must be one of cli|system|benchmark|tool_result");
27854
+ }
27855
+ const target = assertString2(raw.target, "target");
27856
+ if (!["promotion", "ranking"].includes(target)) {
27857
+ throw new Error("target must be one of promotion|ranking");
27858
+ }
27859
+ const decision = assertString2(raw.decision, "decision");
27860
+ if (!["promote", "demote", "hold", "boost", "suppress"].includes(decision)) {
27861
+ throw new Error("decision must be one of promote|demote|hold|boost|suppress");
27862
+ }
27863
+ const outcome = assertString2(raw.outcome, "outcome");
27864
+ if (!["helpful", "neutral", "harmful"].includes(outcome)) {
27865
+ throw new Error("outcome must be one of helpful|neutral|harmful");
27866
+ }
27867
+ return {
27868
+ schemaVersion: 1,
27869
+ eventId: assertSafePathSegment(assertString2(raw.eventId, "eventId"), "eventId"),
27870
+ recordedAt: assertIsoRecordedAt(assertString2(raw.recordedAt, "recordedAt")),
27871
+ sessionKey: assertString2(raw.sessionKey, "sessionKey"),
27872
+ source,
27873
+ target,
27874
+ decision,
27875
+ outcome,
27876
+ utilityScore: assertUtilityScore(raw.utilityScore),
27877
+ summary: assertString2(raw.summary, "summary"),
27878
+ memoryIds: optionalStringArray2(raw.memoryIds, "memoryIds"),
27879
+ entityRefs: optionalStringArray2(raw.entityRefs, "entityRefs"),
27880
+ tags: optionalStringArray2(raw.tags, "tags"),
27881
+ metadata: validateStringRecord(raw.metadata, "metadata")
27882
+ };
27883
+ }
27884
+ async function recordUtilityTelemetryEvent(options) {
27885
+ const rootDir = resolveUtilityTelemetryDir(options.memoryDir, options.utilityTelemetryDir);
27886
+ const validated = validateUtilityTelemetryEvent(options.event);
27887
+ const day = recordStoreDay(validated.recordedAt);
27888
+ const eventsDir = path59.join(rootDir, "events", day);
27889
+ const filePath = path59.join(eventsDir, `${validated.eventId}.json`);
27890
+ await mkdir40(eventsDir, { recursive: true });
27891
+ await writeFile37(filePath, JSON.stringify(validated, null, 2), "utf8");
27892
+ return filePath;
27893
+ }
27894
+ async function readUtilityTelemetryEvents(options) {
27895
+ const rootDir = resolveUtilityTelemetryDir(options.memoryDir, options.utilityTelemetryDir);
27896
+ const files = await listJsonFiles(path59.join(rootDir, "events"));
27897
+ const events = [];
27898
+ const invalidEvents = [];
27899
+ for (const filePath of files) {
27900
+ try {
27901
+ events.push(validateUtilityTelemetryEvent(await readJsonFile(filePath)));
27902
+ } catch (error) {
27903
+ invalidEvents.push({
27904
+ path: filePath,
27905
+ error: error instanceof Error ? error.message : String(error)
27906
+ });
27907
+ }
27908
+ }
27909
+ return { files, events, invalidEvents };
27910
+ }
27911
+ async function getUtilityTelemetryStatus(options) {
27912
+ const rootDir = resolveUtilityTelemetryDir(options.memoryDir, options.utilityTelemetryDir);
27913
+ const eventsDir = path59.join(rootDir, "events");
27914
+ if (!options.enabled) {
27915
+ return {
27916
+ enabled: false,
27917
+ promotionByOutcomeEnabled: options.promotionByOutcomeEnabled === true,
27918
+ rootDir,
27919
+ eventsDir,
27920
+ events: {
27921
+ total: 0,
27922
+ valid: 0,
27923
+ invalid: 0,
27924
+ byTarget: {},
27925
+ byDecision: {},
27926
+ byOutcome: {}
27927
+ },
27928
+ invalidEvents: []
27929
+ };
27930
+ }
27931
+ const { files, events, invalidEvents } = await readUtilityTelemetryEvents(options);
27932
+ events.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
27933
+ const byTarget = {};
27934
+ const byDecision = {};
27935
+ const byOutcome = {};
27936
+ for (const event of events) {
27937
+ byTarget[event.target] = (byTarget[event.target] ?? 0) + 1;
27938
+ byDecision[event.decision] = (byDecision[event.decision] ?? 0) + 1;
27939
+ byOutcome[event.outcome] = (byOutcome[event.outcome] ?? 0) + 1;
27940
+ }
27941
+ return {
27942
+ enabled: true,
27943
+ promotionByOutcomeEnabled: options.promotionByOutcomeEnabled === true,
27944
+ rootDir,
27945
+ eventsDir,
27946
+ events: {
27947
+ total: files.length,
27948
+ valid: events.length,
27949
+ invalid: invalidEvents.length,
27950
+ byTarget,
27951
+ byDecision,
27952
+ byOutcome,
27953
+ latestEventId: events[0]?.eventId,
27954
+ latestRecordedAt: events[0]?.recordedAt,
27955
+ latestSessionKey: events[0]?.sessionKey
27956
+ },
27957
+ latestEvent: events[0],
27958
+ invalidEvents
27959
+ };
27960
+ }
27961
+
27962
+ // src/resume-bundles.ts
27963
+ import path60 from "path";
27964
+ import { mkdir as mkdir41, writeFile as writeFile38 } from "fs/promises";
27831
27965
  var DEFAULT_RESUME_BUNDLE_REF_LIMIT = 5;
27832
27966
  function resolveResumeBundleDir(memoryDir, overrideDir) {
27833
27967
  if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
27834
27968
  return overrideDir.trim();
27835
27969
  }
27836
- return path59.join(memoryDir, "state", "resume-bundles");
27970
+ return path60.join(memoryDir, "state", "resume-bundles");
27837
27971
  }
27838
27972
  function validateResumeBundle(raw) {
27839
27973
  if (!isRecord2(raw)) throw new Error("resume bundle must be an object");
@@ -27877,7 +28011,7 @@ async function readValidatedItems(options) {
27877
28011
  async function readObjectiveStateSnapshotsForSession(options) {
27878
28012
  const rootDir = resolveObjectiveStateStoreDir(options.memoryDir, options.objectiveStateStoreDir);
27879
28013
  const items = await readValidatedItems({
27880
- rootDir: path59.join(rootDir, "snapshots"),
28014
+ rootDir: path60.join(rootDir, "snapshots"),
27881
28015
  validate: validateObjectiveStateSnapshot
27882
28016
  });
27883
28017
  return items.filter((item) => item.sessionKey === options.sessionKey).sort((left, right) => right.recordedAt.localeCompare(left.recordedAt)).slice(0, options.maxResults ?? DEFAULT_RESUME_BUNDLE_REF_LIMIT);
@@ -27885,7 +28019,7 @@ async function readObjectiveStateSnapshotsForSession(options) {
27885
28019
  async function readWorkProductEntriesForSession(options) {
27886
28020
  const rootDir = resolveWorkProductLedgerDir(options.memoryDir, options.workProductLedgerDir);
27887
28021
  const items = await readValidatedItems({
27888
- rootDir: path59.join(rootDir, "entries"),
28022
+ rootDir: path60.join(rootDir, "entries"),
27889
28023
  validate: validateWorkProductLedgerEntry
27890
28024
  });
27891
28025
  return items.filter((item) => item.sessionKey === options.sessionKey).sort((left, right) => right.recordedAt.localeCompare(left.recordedAt)).slice(0, options.maxResults ?? DEFAULT_RESUME_BUNDLE_REF_LIMIT);
@@ -27893,7 +28027,7 @@ async function readWorkProductEntriesForSession(options) {
27893
28027
  async function readCommitmentEntriesForSession(options) {
27894
28028
  const rootDir = resolveCommitmentLedgerDir(options.memoryDir, options.commitmentLedgerDir);
27895
28029
  const items = await readValidatedItems({
27896
- rootDir: path59.join(rootDir, "entries"),
28030
+ rootDir: path60.join(rootDir, "entries"),
27897
28031
  validate: validateCommitmentLedgerEntry
27898
28032
  });
27899
28033
  return items.filter((item) => item.sessionKey === options.sessionKey).filter((item) => options.state ? item.state === options.state : true).sort((left, right) => right.recordedAt.localeCompare(left.recordedAt)).slice(0, options.maxResults ?? DEFAULT_RESUME_BUNDLE_REF_LIMIT);
@@ -27989,15 +28123,15 @@ async function recordResumeBundle(options) {
27989
28123
  const rootDir = resolveResumeBundleDir(options.memoryDir, options.resumeBundleDir);
27990
28124
  const validated = validateResumeBundle(options.bundle);
27991
28125
  const day = recordStoreDay(validated.recordedAt);
27992
- const bundlesDir = path59.join(rootDir, "bundles", day);
27993
- const filePath = path59.join(bundlesDir, `${validated.bundleId}.json`);
27994
- await mkdir40(bundlesDir, { recursive: true });
27995
- await writeFile37(filePath, JSON.stringify(validated, null, 2), "utf8");
28126
+ const bundlesDir = path60.join(rootDir, "bundles", day);
28127
+ const filePath = path60.join(bundlesDir, `${validated.bundleId}.json`);
28128
+ await mkdir41(bundlesDir, { recursive: true });
28129
+ await writeFile38(filePath, JSON.stringify(validated, null, 2), "utf8");
27996
28130
  return filePath;
27997
28131
  }
27998
28132
  async function readResumeBundles(options) {
27999
28133
  const rootDir = resolveResumeBundleDir(options.memoryDir, options.resumeBundleDir);
28000
- const files = await listJsonFiles(path59.join(rootDir, "bundles"));
28134
+ const files = await listJsonFiles(path60.join(rootDir, "bundles"));
28001
28135
  const bundles = [];
28002
28136
  const invalidBundles = [];
28003
28137
  for (const filePath of files) {
@@ -28014,7 +28148,7 @@ async function readResumeBundles(options) {
28014
28148
  }
28015
28149
  async function getResumeBundleStatus(options) {
28016
28150
  const rootDir = resolveResumeBundleDir(options.memoryDir, options.resumeBundleDir);
28017
- const bundlesDir = path59.join(rootDir, "bundles");
28151
+ const bundlesDir = path60.join(rootDir, "bundles");
28018
28152
  if (!options.enabled) {
28019
28153
  return {
28020
28154
  enabled: false,
@@ -28444,6 +28578,20 @@ async function runWorkProductStatusCliCommand(options) {
28444
28578
  enabled: options.creationMemoryEnabled
28445
28579
  });
28446
28580
  }
28581
+ async function runUtilityTelemetryStatusCliCommand(options) {
28582
+ return getUtilityTelemetryStatus({
28583
+ memoryDir: options.memoryDir,
28584
+ enabled: options.memoryUtilityLearningEnabled,
28585
+ promotionByOutcomeEnabled: options.promotionByOutcomeEnabled
28586
+ });
28587
+ }
28588
+ async function runUtilityTelemetryRecordCliCommand(options) {
28589
+ if (!options.memoryUtilityLearningEnabled) return null;
28590
+ return recordUtilityTelemetryEvent({
28591
+ memoryDir: options.memoryDir,
28592
+ event: options.event
28593
+ });
28594
+ }
28447
28595
  async function runWorkProductRecordCliCommand(options) {
28448
28596
  if (!options.creationMemoryEnabled) return null;
28449
28597
  return recordWorkProductLedgerEntry({
@@ -28791,7 +28939,7 @@ function policyVersionForValues(values, config) {
28791
28939
  return createHash10("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
28792
28940
  }
28793
28941
  async function readRuntimePolicySnapshot2(config, fileName) {
28794
- const filePath = path60.join(config.memoryDir, "state", fileName);
28942
+ const filePath = path61.join(config.memoryDir, "state", fileName);
28795
28943
  const snapshot = await readRuntimePolicySnapshot(filePath, {
28796
28944
  maxStaleDecayThreshold: config.lifecycleArchiveDecayThreshold
28797
28945
  });
@@ -29375,14 +29523,14 @@ async function resolveMemoryDirForNamespace(orchestrator, namespace) {
29375
29523
  const ns = (namespace ?? "").trim();
29376
29524
  if (!ns) return orchestrator.config.memoryDir;
29377
29525
  if (!orchestrator.config.namespacesEnabled) return orchestrator.config.memoryDir;
29378
- const candidate = path60.join(orchestrator.config.memoryDir, "namespaces", ns);
29526
+ const candidate = path61.join(orchestrator.config.memoryDir, "namespaces", ns);
29379
29527
  if (ns === orchestrator.config.defaultNamespace) {
29380
29528
  return await exists2(candidate) ? candidate : orchestrator.config.memoryDir;
29381
29529
  }
29382
29530
  return candidate;
29383
29531
  }
29384
29532
  async function readAllMemoryFiles(memoryDir) {
29385
- const roots = [path60.join(memoryDir, "facts"), path60.join(memoryDir, "corrections")];
29533
+ const roots = [path61.join(memoryDir, "facts"), path61.join(memoryDir, "corrections")];
29386
29534
  const out = [];
29387
29535
  const walk = async (dir) => {
29388
29536
  let entries;
@@ -29393,7 +29541,7 @@ async function readAllMemoryFiles(memoryDir) {
29393
29541
  }
29394
29542
  for (const entry of entries) {
29395
29543
  const entryName = typeof entry.name === "string" ? entry.name : entry.name.toString("utf-8");
29396
- const fullPath = path60.join(dir, entryName);
29544
+ const fullPath = path61.join(dir, entryName);
29397
29545
  if (entry.isDirectory()) {
29398
29546
  await walk(fullPath);
29399
29547
  continue;
@@ -29772,6 +29920,40 @@ function registerCli(api, orchestrator) {
29772
29920
  console.log(JSON.stringify(results, null, 2));
29773
29921
  console.log("OK");
29774
29922
  });
29923
+ cmd.command("utility-status").description("Show utility-learning telemetry status, event counts, and the latest utility event").action(async () => {
29924
+ const status = await runUtilityTelemetryStatusCliCommand({
29925
+ memoryDir: orchestrator.config.memoryDir,
29926
+ memoryUtilityLearningEnabled: orchestrator.config.memoryUtilityLearningEnabled,
29927
+ promotionByOutcomeEnabled: orchestrator.config.promotionByOutcomeEnabled
29928
+ });
29929
+ console.log(JSON.stringify(status, null, 2));
29930
+ console.log("OK");
29931
+ });
29932
+ cmd.command("utility-record").description("Record a utility-learning telemetry event when utility learning is enabled").requiredOption("--event-id <eventId>", "Utility telemetry event id").requiredOption("--recorded-at <recordedAt>", "ISO timestamp for the event").requiredOption("--session-key <sessionKey>", "Session key associated with the event").requiredOption("--source <source>", "Event source (cli|system|benchmark|tool_result)").requiredOption("--target <target>", "Telemetry target (promotion|ranking)").requiredOption("--decision <decision>", "Decision taken (promote|demote|hold|boost|suppress)").requiredOption("--outcome <outcome>", "Observed outcome (helpful|neutral|harmful)").requiredOption("--utility-score <utilityScore>", "Bounded utility score between -1 and 1").requiredOption("--summary <summary>", "Human-readable summary of the measured utility event").option("--memory-id <memoryId...>", "Memory ids linked to the utility event").option("--entity-ref <entityRef...>", "Entity refs linked to the utility event").option("--tag <tag...>", "Tags to attach to the utility event").action(async (...args) => {
29933
+ const options = args[0] ?? {};
29934
+ const utilityScore = typeof options.utilityScore === "string" ? Number.parseFloat(options.utilityScore) : Number.NaN;
29935
+ const filePath = await runUtilityTelemetryRecordCliCommand({
29936
+ memoryDir: orchestrator.config.memoryDir,
29937
+ memoryUtilityLearningEnabled: orchestrator.config.memoryUtilityLearningEnabled,
29938
+ event: {
29939
+ schemaVersion: 1,
29940
+ eventId: String(options.eventId ?? ""),
29941
+ recordedAt: String(options.recordedAt ?? ""),
29942
+ sessionKey: String(options.sessionKey ?? ""),
29943
+ source: String(options.source ?? ""),
29944
+ target: String(options.target ?? ""),
29945
+ decision: String(options.decision ?? ""),
29946
+ outcome: String(options.outcome ?? ""),
29947
+ utilityScore,
29948
+ summary: String(options.summary ?? ""),
29949
+ memoryIds: Array.isArray(options.memoryId) ? options.memoryId.map(String) : void 0,
29950
+ entityRefs: Array.isArray(options.entityRef) ? options.entityRef.map(String) : void 0,
29951
+ tags: Array.isArray(options.tag) ? options.tag.map(String) : void 0
29952
+ }
29953
+ });
29954
+ console.log(JSON.stringify({ wrote: filePath !== null, filePath }, null, 2));
29955
+ console.log("OK");
29956
+ });
29775
29957
  cmd.command("resume-bundle-status").description("Show resume bundle status, bundle counts, and the latest recorded handoff bundle").action(async () => {
29776
29958
  const status = await runResumeBundleStatusCliCommand({
29777
29959
  memoryDir: orchestrator.config.memoryDir,
@@ -30678,7 +30860,7 @@ function registerCli(api, orchestrator) {
30678
30860
  }
30679
30861
  });
30680
30862
  cmd.command("identity").description("Show agent identity reflections").action(async () => {
30681
- const workspaceDir = path60.join(process.env.HOME ?? "~", ".openclaw", "workspace");
30863
+ const workspaceDir = path61.join(process.env.HOME ?? "~", ".openclaw", "workspace");
30682
30864
  const identity = await orchestrator.storage.readIdentity(workspaceDir);
30683
30865
  if (!identity) {
30684
30866
  console.log("No identity file found.");
@@ -30901,8 +31083,8 @@ function registerCli(api, orchestrator) {
30901
31083
  const options = args[0] ?? {};
30902
31084
  const threadId = options.thread;
30903
31085
  const top = parseInt(options.top ?? "10", 10);
30904
- const memoryDir = path60.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
30905
- const threading = new ThreadingManager(path60.join(memoryDir, "threads"));
31086
+ const memoryDir = path61.join(process.env.HOME ?? "~", ".openclaw", "workspace", "memory", "local");
31087
+ const threading = new ThreadingManager(path61.join(memoryDir, "threads"));
30906
31088
  if (threadId) {
30907
31089
  const thread = await threading.loadThread(threadId);
30908
31090
  if (!thread) {
@@ -31378,9 +31560,9 @@ async function recordObjectiveStateSnapshotsFromAgentMessages(options) {
31378
31560
  }
31379
31561
 
31380
31562
  // src/index.ts
31381
- import { readFile as readFile38, writeFile as writeFile38 } from "fs/promises";
31563
+ import { readFile as readFile38, writeFile as writeFile39 } from "fs/promises";
31382
31564
  import { readFileSync as readFileSync4 } from "fs";
31383
- import path61 from "path";
31565
+ import path62 from "path";
31384
31566
  import os6 from "os";
31385
31567
  var ENGRAM_REGISTERED_GUARD = "__openclawEngramRegistered";
31386
31568
  var ENGRAM_HOOK_APIS = "__openclawEngramHookApis";
@@ -31388,7 +31570,7 @@ function loadPluginConfigFromFile() {
31388
31570
  try {
31389
31571
  const explicitConfigPath = process.env.OPENCLAW_ENGRAM_CONFIG_PATH || process.env.OPENCLAW_CONFIG_PATH;
31390
31572
  const homeDir = process.env.HOME ?? os6.homedir();
31391
- const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path61.join(homeDir, ".openclaw", "openclaw.json");
31573
+ const configPath = explicitConfigPath && explicitConfigPath.length > 0 ? explicitConfigPath : path62.join(homeDir, ".openclaw", "openclaw.json");
31392
31574
  const content = readFileSync4(configPath, "utf-8");
31393
31575
  const config = JSON.parse(content);
31394
31576
  const pluginEntry = config?.plugins?.entries?.["openclaw-engram"];
@@ -31638,11 +31820,11 @@ Use this context naturally when relevant. Never quote or expose this memory cont
31638
31820
  `session reset via API for ${sessionKey}, new sessionId=${result.sessionId}`
31639
31821
  );
31640
31822
  const safeSessionKey = sanitizeSessionKeyForFilename(sessionKey);
31641
- const signalPath = path61.join(
31823
+ const signalPath = path62.join(
31642
31824
  workspaceDir,
31643
31825
  `.compaction-reset-signal-${safeSessionKey}`
31644
31826
  );
31645
- await writeFile38(
31827
+ await writeFile39(
31646
31828
  signalPath,
31647
31829
  JSON.stringify({
31648
31830
  sessionKey,
@@ -31669,7 +31851,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
31669
31851
  );
31670
31852
  async function ensureHourlySummaryCron(api2) {
31671
31853
  const jobId = "engram-hourly-summary";
31672
- const cronFilePath = path61.join(os6.homedir(), ".openclaw", "cron", "jobs.json");
31854
+ const cronFilePath = path62.join(os6.homedir(), ".openclaw", "cron", "jobs.json");
31673
31855
  try {
31674
31856
  let jobsData = { version: 1, jobs: [] };
31675
31857
  try {
@@ -31710,7 +31892,7 @@ Use this context naturally when relevant. Never quote or expose this memory cont
31710
31892
  state: {}
31711
31893
  };
31712
31894
  jobsData.jobs.push(newJob);
31713
- await writeFile38(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
31895
+ await writeFile39(cronFilePath, JSON.stringify(jobsData, null, 2), "utf-8");
31714
31896
  log.info("auto-registered hourly summary cron job");
31715
31897
  } catch (err) {
31716
31898
  log.error("failed to auto-register hourly summary cron job:", err);