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 +28 -0
- package/README.md +2 -2
- package/dist/src/dreamer.js +1 -1
- package/dist/src/index.js +7 -4
- package/dist/src/memory.d.ts +10 -4
- package/dist/src/memory.js +96 -15
- package/dist/src/topics.js +5 -4
- package/dist/src/types.d.ts +25 -4
- package/dist/test/dreamer.test.js +1 -0
- package/dist/test/memory.test.js +11 -15
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
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
|
[](https://www.typescriptlang.org/)
|
|
4
4
|
[](https://nodejs.org/)
|
|
5
|
-
[](https://github.com/openclaw)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](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)
|
|
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
|
|
package/dist/src/dreamer.js
CHANGED
|
@@ -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
|
-
|
|
347
|
-
|
|
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(
|
|
382
|
+
remember(memoryEntry, "interaction");
|
|
380
383
|
}
|
|
381
384
|
}
|
|
382
385
|
catch (err) {
|
package/dist/src/memory.d.ts
CHANGED
|
@@ -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
|
|
38
|
-
*
|
|
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(
|
|
46
|
+
export declare function remember(summaryOrEntry: string | MemoryEntry, fullContextOrCategory?: Record<string, unknown> | string, category?: string): void;
|
|
41
47
|
//# sourceMappingURL=memory.d.ts.map
|
package/dist/src/memory.js
CHANGED
|
@@ -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: {
|
|
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: {
|
|
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
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
|
204
|
-
*
|
|
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(
|
|
207
|
-
|
|
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
|
package/dist/src/topics.js
CHANGED
|
@@ -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 =
|
|
33
|
-
|
|
34
|
-
:
|
|
35
|
-
|
|
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
|
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -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:
|
|
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");
|
package/dist/test/memory.test.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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
|
|
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: {
|
|
110
|
+
content: { text_summary: "", timestamp: Date.now() },
|
|
114
111
|
},
|
|
115
112
|
];
|
|
116
113
|
const ctx = formatDeepMemoryContext(memories);
|
|
117
|
-
assert.ok(ctx.includes("
|
|
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(
|
|
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
|
|
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.
|
|
131
|
-
assert.ok(match, "Expected to find memory with
|
|
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(() => {
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclawdreams",
|
|
3
3
|
"name": "openclawdreams",
|
|
4
4
|
"displayName": "ElectricSheep",
|
|
5
|
-
"version": "1.6.
|
|
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": [
|