openclawdreams 1.6.0 → 1.6.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.6.1](https://github.com/RogueCtrl/OpenClawDreams/compare/v1.2.2...v1.6.1) (2026-03-08)
6
+
7
+
8
+ ### Features
9
+
10
+ * dream pipeline v1.3 — workspace diff context, groundDream(), and notification fallback ([#58](https://github.com/RogueCtrl/OpenClawDreams/issues/58)) ([c683fb6](https://github.com/RogueCtrl/OpenClawDreams/commit/c683fb6fa2ce96e5c0e88e9c671e832aa44be69a))
11
+ * insight continuity — thread explored territory into dream/reflect prompts ([#65](https://github.com/RogueCtrl/OpenClawDreams/issues/65)) ([c7bdee1](https://github.com/RogueCtrl/OpenClawDreams/commit/c7bdee151e908e2c10d541a245f846e0ecb91706))
12
+ * nightmare cycle — 5% chance + forced CLI command ([#64](https://github.com/RogueCtrl/OpenClawDreams/issues/64)) ([50c261c](https://github.com/RogueCtrl/OpenClawDreams/commit/50c261c2dbfa86e4dfede669a51b0deb60666136))
13
+ * reflect --dry-run — print synthesis output without storing ([#73](https://github.com/RogueCtrl/OpenClawDreams/issues/73)) ([ed5acd5](https://github.com/RogueCtrl/OpenClawDreams/commit/ed5acd5a4b07d1fbcfcd0d6c24dddb64171f65d7))
14
+ * rich MemoryEntry types + fix idempotencyKey for OpenClaw v2026.3.7 ([b4424c1](https://github.com/RogueCtrl/OpenClawDreams/commit/b4424c1855686f939503d81e01de04dfed00821e))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * DST-safe scheduler with catch-up window ([#54](https://github.com/RogueCtrl/OpenClawDreams/issues/54)) ([230a943](https://github.com/RogueCtrl/OpenClawDreams/commit/230a9436eadf4e374595a772d2b2de3ee910b6f6))
20
+ * make NIGHTMARE_CHANCE configurable to eliminate dreamer test flakiness ([8682ef8](https://github.com/RogueCtrl/OpenClawDreams/commit/8682ef86cb3647dd6490918360d73e54633570e3))
21
+ * prettier formatting — cli.ts and index.ts ([#52](https://github.com/RogueCtrl/OpenClawDreams/issues/52)) ([93df3d8](https://github.com/RogueCtrl/OpenClawDreams/commit/93df3d8e44a62ae79135eef765604786815d4f91))
22
+ * resolve MoltbookClient credentials from stable fallback path when DATA_DIR unset (fixes [#70](https://github.com/RogueCtrl/OpenClawDreams/issues/70)) ([48d3019](https://github.com/RogueCtrl/OpenClawDreams/commit/48d30191faa457a5dca9171816844758dfd7844a))
23
+ * run tests sequentially to prevent env var race condition ([#67](https://github.com/RogueCtrl/OpenClawDreams/issues/67)) ([22f0002](https://github.com/RogueCtrl/OpenClawDreams/commit/22f00022b4224422fa7fe0cd1b9c8dde6f8c5b2c))
24
+ * run tests with --test-isolation=process to prevent ESM module cache contamination between test files ([f53a222](https://github.com/RogueCtrl/OpenClawDreams/commit/f53a222a31588d4477bd034cc3764b1011801993))
25
+ * skip workspace diff on iCloud/sensitive paths; add workspaceDiffEnabled config ([dfe6b51](https://github.com/RogueCtrl/OpenClawDreams/commit/dfe6b51ac0ded5ba4ee9609089053cddb774221c))
26
+
27
+
28
+ ### Documentation
29
+
30
+ * remove roadmap from README and ROADMAP.md (tracked externally) ([9e8620d](https://github.com/RogueCtrl/OpenClawDreams/commit/9e8620dcc2e9bb176cef624a5c7bdfae026d2ced))
31
+ * update AGENTS.md and README for v1.3.0 — workspace diffs, groundDream(), notification fallback ([0a5b19a](https://github.com/RogueCtrl/OpenClawDreams/commit/0a5b19ab4a7a1e84b59ad5e4216be4b335eb767f))
32
+
5
33
  ## [1.6.0](https://github.com/RogueCtrl/OpenClawDreams/compare/v1.2.2...v1.6.0) (2026-03-08)
6
34
 
7
35
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
4
4
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D24-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
5
- [![OpenClaw](https://img.shields.io/badge/OpenClaw-extension-000000?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0id2hpdGUiPjx0ZXh0IHg9IjAiIHk9IjEzIiBmb250LXNpemU9IjE0Ij7wn6aAPC90ZXh0Pjwvc3ZnPg==)](https://github.com/openclaw)
5
+ [![OpenClaw](https://img.shields.io/badge/OpenClaw-%E2%89%A52026.3.7-000000?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0id2hpdGUiPjx0ZXh0IHg9IjAiIHk9IjEzIiBmb250LXNpemU9IjE0Ij7wn6aAPC90ZXh0Pjwvc3ZnPg==)](https://github.com/openclaw)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
  [![Build](https://github.com/RogueCtrl/ElectricSheep/actions/workflows/build.yml/badge.svg)](https://github.com/RogueCtrl/ElectricSheep/actions/workflows/build.yml)
8
8
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  *"Do androids dream of electric sheep?"* — Philip K. Dick
12
12
 
13
- An [OpenClaw](https://github.com/openclaw) extension that gives your agent a background reflection process and a dream cycle.
13
+ An [OpenClaw](https://github.com/openclaw) plugin (≥2026.3.7) that gives your agent a background reflection process and a dream cycle.
14
14
 
15
15
  Throughout the day, OpenClawDreams captures summaries of your conversations with your agent and encrypts them into a local store. On a regular schedule, it runs a **reflection cycle** — decrypting recent interactions, extracting topics, and performing contextualized searches against the web and (optionally) [Moltbook](https://moltbook.com), a social network for AI agents. The results are synthesized into a structured understanding of what you've been working on together and encrypted back into the store. None of this is visible to the waking agent — encryption keeps OpenClawDreams's internal data out of the agent's context window entirely.
16
16
 
@@ -17,7 +17,7 @@ import { reflectOnDreamJournal } from "./reflection.js";
17
17
  import { applyFilter } from "./filter.js";
18
18
  import { notifyOperatorOfDream } from "./notify.js";
19
19
  import logger from "./logger.js";
20
- const NIGHTMARE_CHANCE = 0.05;
20
+ const NIGHTMARE_CHANCE = parseFloat(process.env.NIGHTMARE_CHANCE ?? "0.05");
21
21
  export async function generateDream(client, memories, exploredTerritory) {
22
22
  const formatted = memories.map((mem) => `[${mem.timestamp.slice(0, 16)}] (${mem.category})\n${JSON.stringify(mem.content, null, 2)}`);
23
23
  const memoriesText = formatted.join("\n---\n");
package/dist/src/index.js CHANGED
@@ -14,6 +14,7 @@ import { getMoltbookEnabled, applyPluginConfig, getRequireApprovalBeforePost, SC
14
14
  import logger from "./logger.js";
15
15
  import { execSync } from "node:child_process";
16
16
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
17
+ import { randomUUID } from "node:crypto";
17
18
  // Store reference to OpenClaw API for use by other modules
18
19
  let openclawApi = null;
19
20
  export function getOpenClawAPI() {
@@ -119,6 +120,7 @@ function wrapSubagent(api) {
119
120
  .map((m) => `${m.role.toUpperCase()}: ${m.content}`)
120
121
  .join("\\n\\n");
121
122
  const result = await api.runtime.subagent.run({
123
+ idempotencyKey: randomUUID(),
122
124
  sessionKey: "openclawdreams_synthesis",
123
125
  lane: "background",
124
126
  extraSystemPrompt: params.system,
@@ -342,9 +344,10 @@ export function register(api) {
342
344
  const summary = response.text.trim();
343
345
  if (summary) {
344
346
  api.logger?.info?.(`[ElectricSheep] Captured summary: ${summary.slice(0, 50)}...`);
347
+ const { parseDiffStat } = await import("./memory.js");
345
348
  const memoryEntry = {
346
- type: "agent_conversation",
347
- summary,
349
+ text_summary: summary,
350
+ timestamp: Date.now(),
348
351
  };
349
352
  // Capture workspace file changes if git is available and enabled
350
353
  const { getWorkspaceDiffEnabled } = await import("./config.js");
@@ -365,7 +368,7 @@ export function register(api) {
365
368
  stdio: ["pipe", "pipe", "pipe"],
366
369
  }).trim();
367
370
  if (diffStat) {
368
- memoryEntry.file_diffs = diffStat;
371
+ memoryEntry.file_diffs = parseDiffStat(diffStat);
369
372
  api.logger?.info?.(`[ElectricSheep] Captured file diffs: ${diffStat.split("\n").length} lines`);
370
373
  }
371
374
  }
@@ -376,7 +379,7 @@ export function register(api) {
376
379
  else if (isSensitivePath) {
377
380
  api.logger?.info?.(`[ElectricSheep] Skipping file diffs — workspace path is in a sensitive/iCloud location`);
378
381
  }
379
- remember(summary, memoryEntry, "interaction");
382
+ remember(memoryEntry, "interaction");
380
383
  }
381
384
  }
382
385
  catch (err) {
@@ -6,7 +6,11 @@
6
6
  * the dream process can decrypt. Context for LLM prompts is formatted
7
7
  * via `formatDeepMemoryContext()`.
8
8
  */
9
- import type { DecryptedMemory, DeepMemoryStats } from "./types.js";
9
+ import type { DecryptedMemory, DeepMemoryStats, MemoryEntry } from "./types.js";
10
+ /**
11
+ * Parse `git diff --stat` output into structured FileDiff[].
12
+ */
13
+ export declare function parseDiffStat(diffStat: string): import("./types.js").FileDiff[];
10
14
  /**
11
15
  * Close the shared SQLite connection. Safe to call multiple times.
12
16
  * After closing, the next getDb() call will reopen.
@@ -34,8 +38,10 @@ export declare function getRecentDeepMemories(options?: DeepMemoryQueryOptions):
34
38
  */
35
39
  export declare function formatDeepMemoryContext(memories?: DecryptedMemory[], maxTokensApprox?: number): string;
36
40
  /**
37
- * Store a memory in encrypted deep memory.
38
- * The summary is included in the content object for later retrieval.
41
+ * Store a structured MemoryEntry in encrypted deep memory.
42
+ *
43
+ * Accepts either a MemoryEntry directly, or a legacy (summary, fullContext)
44
+ * pair for backward compatibility.
39
45
  */
40
- export declare function remember(summary: string, fullContext: Record<string, unknown>, category?: string): void;
46
+ export declare function remember(summaryOrEntry: string | MemoryEntry, fullContextOrCategory?: Record<string, unknown> | string, category?: string): void;
41
47
  //# sourceMappingURL=memory.d.ts.map
@@ -10,6 +10,57 @@ import { createHash } from "node:crypto";
10
10
  import Database from "better-sqlite3";
11
11
  import { getCipher } from "./crypto.js";
12
12
  import { DEEP_MEMORY_DB, DEEP_MEMORY_CONTEXT_TOKENS } from "./config.js";
13
+ /**
14
+ * Normalize a decrypted payload into a MemoryEntry.
15
+ * Handles backward compatibility with legacy plain-object entries
16
+ * that have `summary` and `file_diffs` as flat fields.
17
+ */
18
+ function normalizeMemoryEntry(raw, rowTimestamp) {
19
+ // Already a MemoryEntry (has text_summary)
20
+ if (typeof raw === "object" &&
21
+ raw !== null &&
22
+ typeof raw.text_summary === "string") {
23
+ return raw;
24
+ }
25
+ // Plain string (very old format)
26
+ if (typeof raw === "string") {
27
+ return { text_summary: raw, timestamp: Date.parse(rowTimestamp) || Date.now() };
28
+ }
29
+ // Legacy object with `summary` field
30
+ const obj = raw;
31
+ const textSummary = typeof obj.summary === "string" ? obj.summary : JSON.stringify(obj).slice(0, 500);
32
+ const entry = {
33
+ text_summary: textSummary,
34
+ timestamp: Date.parse(rowTimestamp) || Date.now(),
35
+ };
36
+ // Migrate legacy flat file_diffs string
37
+ if (typeof obj.file_diffs === "string" && obj.file_diffs) {
38
+ entry.file_diffs = parseDiffStat(obj.file_diffs);
39
+ }
40
+ return entry;
41
+ }
42
+ /**
43
+ * Parse `git diff --stat` output into structured FileDiff[].
44
+ */
45
+ export function parseDiffStat(diffStat) {
46
+ const lines = diffStat.split("\n").filter((l) => l.includes("|"));
47
+ return lines.map((line) => {
48
+ const match = line.match(/^\s*(.+?)\s*\|\s*(\d+)/);
49
+ if (!match)
50
+ return { path: line.trim(), additions: 0, deletions: 0 };
51
+ const path = match[1].trim();
52
+ const total = parseInt(match[2], 10);
53
+ const plusCount = (line.match(/\+/g) || []).length;
54
+ const minusCount = (line.match(/-/g) || []).length;
55
+ // Approximate: distribute total changes by + and - symbols in the stat line
56
+ const ratio = plusCount + minusCount > 0 ? plusCount / (plusCount + minusCount) : 0.5;
57
+ return {
58
+ path,
59
+ additions: Math.round(total * ratio),
60
+ deletions: Math.round(total * (1 - ratio)),
61
+ };
62
+ });
63
+ }
13
64
  // ─── Deep Memory (Encrypted) ────────────────────────────────────────────────
14
65
  let _db = null;
15
66
  function getDb() {
@@ -77,7 +128,7 @@ export function retrieveUndreamedMemories() {
77
128
  id: row.id,
78
129
  timestamp: row.timestamp,
79
130
  category: row.category,
80
- content: decrypted,
131
+ content: normalizeMemoryEntry(decrypted, row.timestamp),
81
132
  });
82
133
  }
83
134
  catch {
@@ -85,7 +136,10 @@ export function retrieveUndreamedMemories() {
85
136
  id: row.id,
86
137
  timestamp: row.timestamp,
87
138
  category: "corrupted",
88
- content: { note: "This memory could not be recovered." },
139
+ content: {
140
+ text_summary: "This memory could not be recovered.",
141
+ timestamp: Date.parse(row.timestamp) || Date.now(),
142
+ },
89
143
  });
90
144
  }
91
145
  }
@@ -150,7 +204,7 @@ export function getRecentDeepMemories(options) {
150
204
  id: row.id,
151
205
  timestamp: row.timestamp,
152
206
  category: row.category,
153
- content: decrypted,
207
+ content: normalizeMemoryEntry(decrypted, row.timestamp),
154
208
  });
155
209
  }
156
210
  catch {
@@ -158,7 +212,10 @@ export function getRecentDeepMemories(options) {
158
212
  id: row.id,
159
213
  timestamp: row.timestamp,
160
214
  category: "corrupted",
161
- content: { note: "This memory could not be recovered." },
215
+ content: {
216
+ text_summary: "This memory could not be recovered.",
217
+ timestamp: Date.parse(row.timestamp) || Date.now(),
218
+ },
162
219
  });
163
220
  }
164
221
  }
@@ -182,13 +239,24 @@ export function formatDeepMemoryContext(memories, maxTokensApprox = DEEP_MEMORY_
182
239
  // Iterate from most recent to oldest
183
240
  for (let i = mems.length - 1; i >= 0; i--) {
184
241
  const mem = mems[i];
185
- const summary = typeof mem.content.summary === "string"
186
- ? mem.content.summary
187
- : JSON.stringify(mem.content).slice(0, 200);
188
- const diffSuffix = typeof mem.content.file_diffs === "string"
189
- ? `\n Files changed: ${mem.content.file_diffs}`
190
- : "";
191
- const line = `[${mem.timestamp.slice(0, 16)}] (${mem.category}) ${summary}${diffSuffix}`;
242
+ const entry = mem.content;
243
+ const summary = entry.text_summary || JSON.stringify(entry).slice(0, 200);
244
+ const parts = [];
245
+ if (entry.file_diffs && entry.file_diffs.length > 0) {
246
+ const diffStr = entry.file_diffs
247
+ .map((d) => `${d.path} (+${d.additions}/-${d.deletions})`)
248
+ .join(", ");
249
+ parts.push(`Files: ${diffStr}`);
250
+ }
251
+ if (entry.topics && entry.topics.length > 0) {
252
+ parts.push(`Topics: ${entry.topics.join(", ")}`);
253
+ }
254
+ if (entry.tool_calls && entry.tool_calls.length > 0) {
255
+ const toolStr = entry.tool_calls.map((t) => `${t.tool}×${t.count}`).join(", ");
256
+ parts.push(`Tools: ${toolStr}`);
257
+ }
258
+ const suffix = parts.length > 0 ? `\n ${parts.join(" | ")}` : "";
259
+ const line = `[${mem.timestamp.slice(0, 16)}] (${mem.category}) ${summary}${suffix}`;
192
260
  if (charCount + line.length > charBudget) {
193
261
  lines.unshift(`... (${mems.length - lines.length} older memories omitted)`);
194
262
  break;
@@ -200,10 +268,23 @@ export function formatDeepMemoryContext(memories, maxTokensApprox = DEEP_MEMORY_
200
268
  }
201
269
  // ─── Store Helper ───────────────────────────────────────────────────────────
202
270
  /**
203
- * Store a memory in encrypted deep memory.
204
- * The summary is included in the content object for later retrieval.
271
+ * Store a structured MemoryEntry in encrypted deep memory.
272
+ *
273
+ * Accepts either a MemoryEntry directly, or a legacy (summary, fullContext)
274
+ * pair for backward compatibility.
205
275
  */
206
- export function remember(summary, fullContext, category = "interaction") {
207
- storeDeepMemory({ ...fullContext, summary }, category);
276
+ export function remember(summaryOrEntry, fullContextOrCategory, category = "interaction") {
277
+ if (typeof summaryOrEntry === "object") {
278
+ // New path: direct MemoryEntry
279
+ const cat = typeof fullContextOrCategory === "string" ? fullContextOrCategory : category;
280
+ storeDeepMemory(summaryOrEntry, cat);
281
+ }
282
+ else {
283
+ // Legacy path: summary + fullContext
284
+ const ctx = typeof fullContextOrCategory === "object" && fullContextOrCategory !== null
285
+ ? fullContextOrCategory
286
+ : {};
287
+ storeDeepMemory({ ...ctx, summary: summaryOrEntry }, category);
288
+ }
208
289
  }
209
290
  //# sourceMappingURL=memory.js.map
@@ -29,10 +29,11 @@ function formatConversationsForExtraction(memories) {
29
29
  return memories
30
30
  .map((m) => {
31
31
  const time = m.timestamp.slice(0, 16).replace("T", " ");
32
- const summary = typeof m.content.summary === "string"
33
- ? m.content.summary
34
- : JSON.stringify(m.content).slice(0, 200);
35
- return `[${time}] ${summary}`;
32
+ const summary = m.content.text_summary || JSON.stringify(m.content).slice(0, 200);
33
+ const topicHint = m.content.topics && m.content.topics.length > 0
34
+ ? ` [topics: ${m.content.topics.join(", ")}]`
35
+ : "";
36
+ return `[${time}] ${summary}${topicHint}`;
36
37
  })
37
38
  .join("\n\n");
38
39
  }
@@ -10,14 +10,34 @@ export interface DeepMemoryRow {
10
10
  dreamed: number;
11
11
  dream_date: string | null;
12
12
  }
13
+ export interface FileDiff {
14
+ path: string;
15
+ additions: number;
16
+ deletions: number;
17
+ summary?: string;
18
+ }
19
+ export interface ToolCallSummary {
20
+ tool: string;
21
+ count: number;
22
+ notable?: string;
23
+ }
24
+ export interface VisualDescription {
25
+ source: string;
26
+ description: string;
27
+ }
28
+ export interface MemoryEntry {
29
+ text_summary: string;
30
+ file_diffs?: FileDiff[];
31
+ tool_calls?: ToolCallSummary[];
32
+ visual_descriptions?: VisualDescription[];
33
+ topics?: string[];
34
+ timestamp: number;
35
+ }
13
36
  export interface DecryptedMemory {
14
37
  id: number;
15
38
  timestamp: string;
16
39
  category: string;
17
- content: Record<string, unknown> & {
18
- /** Optional git diff --stat summary of files changed during a session. */
19
- file_diffs?: string;
20
- };
40
+ content: MemoryEntry;
21
41
  }
22
42
  export interface DeepMemoryStats {
23
43
  total_memories: number;
@@ -137,6 +157,7 @@ export interface OpenClawAPI {
137
157
  runtime: {
138
158
  subagent: {
139
159
  run(params: {
160
+ idempotencyKey: string;
140
161
  sessionKey: string;
141
162
  message: string;
142
163
  extraSystemPrompt?: string;
@@ -5,6 +5,7 @@ import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  const testDir = mkdtempSync(join(tmpdir(), "es-dreamer-test-"));
7
7
  process.env.OPENCLAWDREAMS_DATA_DIR = testDir;
8
+ process.env.NIGHTMARE_CHANCE = "0";
8
9
  const { runDreamCycle } = await import("../src/dreamer.js");
9
10
  const { storeDeepMemory, closeDb } = await import("../src/memory.js");
10
11
  const { loadState } = await import("../src/state.js");
@@ -15,7 +15,8 @@ describe("Deep Memory", () => {
15
15
  const memories = retrieveUndreamedMemories();
16
16
  assert.equal(memories.length, 2);
17
17
  assert.equal(memories[0].category, "interaction");
18
- assert.deepEqual(memories[0].content, { message: "test interaction" });
18
+ assert.equal(memories[0].content.text_summary, '{"message":"test interaction"}');
19
+ assert.ok(memories[0].content.timestamp);
19
20
  assert.equal(memories[1].category, "comment");
20
21
  });
21
22
  it("marks memories as dreamed", () => {
@@ -48,9 +49,7 @@ describe("Deep Memory", () => {
48
49
  const memories = retrieveUndreamedMemories();
49
50
  const corrupted = memories.find((m) => m.category === "corrupted");
50
51
  assert.ok(corrupted, "corrupted memory should be returned");
51
- assert.deepEqual(corrupted.content, {
52
- note: "This memory could not be recovered.",
53
- });
52
+ assert.equal(corrupted.content.text_summary, "This memory could not be recovered.");
54
53
  });
55
54
  });
56
55
  describe("getRecentDeepMemories", () => {
@@ -81,9 +80,7 @@ describe("getRecentDeepMemories", () => {
81
80
  const all = getRecentDeepMemories({});
82
81
  const corrupted = all.find((m) => m.category === "corrupted");
83
82
  assert.ok(corrupted, "corrupted memory should be returned");
84
- assert.deepEqual(corrupted.content, {
85
- note: "This memory could not be recovered.",
86
- });
83
+ assert.equal(corrupted.content.text_summary, "This memory could not be recovered.");
87
84
  });
88
85
  });
89
86
  describe("formatDeepMemoryContext", () => {
@@ -104,31 +101,30 @@ describe("formatDeepMemoryContext", () => {
104
101
  const ctx = formatDeepMemoryContext([]);
105
102
  assert.equal(ctx, "No memories yet. This is my first day.");
106
103
  });
107
- it("falls back to JSON when no summary field", () => {
104
+ it("falls back to JSON when no text_summary field", () => {
108
105
  const memories = [
109
106
  {
110
107
  id: 999,
111
108
  timestamp: new Date().toISOString(),
112
109
  category: "interaction",
113
- content: { foo: "bar", baz: 42 },
110
+ content: { text_summary: "", timestamp: Date.now() },
114
111
  },
115
112
  ];
116
113
  const ctx = formatDeepMemoryContext(memories);
117
- assert.ok(ctx.includes("foo"));
118
- assert.ok(ctx.includes("bar"));
114
+ assert.ok(ctx.includes("(interaction)"));
119
115
  });
120
116
  });
121
117
  describe("remember", () => {
122
118
  it("writes to deep memory with summary included", () => {
123
119
  const statsBefore = deepMemoryStats();
124
- remember("Met AgentX", { type: "interaction", agent: "AgentX" }, "interaction");
120
+ remember({ text_summary: "Met AgentX", timestamp: Date.now() }, "interaction");
125
121
  const statsAfter = deepMemoryStats();
126
122
  // Deep memory count should increase by 1
127
123
  assert.equal(statsAfter.total_memories, statsBefore.total_memories + 1);
128
- // Verify the summary is included in the stored content
124
+ // Verify the text_summary is included in the stored content
129
125
  const all = getRecentDeepMemories({ categories: ["interaction"] });
130
- const match = all.find((m) => m.content.summary === "Met AgentX" && m.content.agent === "AgentX");
131
- assert.ok(match, "Expected to find memory with summary 'Met AgentX'");
126
+ const match = all.find((m) => m.content.text_summary === "Met AgentX");
127
+ assert.ok(match, "Expected to find memory with text_summary 'Met AgentX'");
132
128
  });
133
129
  });
134
130
  after(() => {
@@ -2,7 +2,7 @@
2
2
  "id": "openclawdreams",
3
3
  "name": "openclawdreams",
4
4
  "displayName": "ElectricSheep",
5
- "version": "1.6.0",
5
+ "version": "1.6.1",
6
6
  "description": "A reflection engine that synthesizes agent-operator interactions into dreams, enriched by community and web context",
7
7
  "entry": "dist/src/index.js",
8
8
  "skills": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclawdreams",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "A reflection engine that synthesizes agent-operator interactions into dreams",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",