openclawdreams 0.7.0
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/.env.example +14 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/dependabot.yml +17 -0
- package/.github/pull_request_template.md +19 -0
- package/.github/workflows/build.yml +30 -0
- package/.github/workflows/release.yml +110 -0
- package/.prettierignore +4 -0
- package/.prettierrc +7 -0
- package/.versionrc.json +26 -0
- package/AGENTS.md +286 -0
- package/CHANGELOG.md +157 -0
- package/CODE_OF_CONDUCT.md +41 -0
- package/CONTRIBUTING.md +95 -0
- package/LICENSE +21 -0
- package/README.md +363 -0
- package/SECURITY.md +39 -0
- package/bin/electricsheep.ts +5 -0
- package/dist/bin/electricsheep.d.ts +3 -0
- package/dist/bin/electricsheep.d.ts.map +1 -0
- package/dist/bin/electricsheep.js +4 -0
- package/dist/bin/electricsheep.js.map +1 -0
- package/dist/src/budget.d.ts +28 -0
- package/dist/src/budget.d.ts.map +1 -0
- package/dist/src/budget.js +87 -0
- package/dist/src/budget.js.map +1 -0
- package/dist/src/cli.d.ts +19 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +289 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +37 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +70 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/crypto.d.ts +19 -0
- package/dist/src/crypto.d.ts.map +1 -0
- package/dist/src/crypto.js +70 -0
- package/dist/src/crypto.js.map +1 -0
- package/dist/src/dreamer.d.ts +13 -0
- package/dist/src/dreamer.d.ts.map +1 -0
- package/dist/src/dreamer.js +213 -0
- package/dist/src/dreamer.js.map +1 -0
- package/dist/src/filter.d.ts +30 -0
- package/dist/src/filter.d.ts.map +1 -0
- package/dist/src/filter.js +124 -0
- package/dist/src/filter.js.map +1 -0
- package/dist/src/identity.d.ts +29 -0
- package/dist/src/identity.d.ts.map +1 -0
- package/dist/src/identity.js +83 -0
- package/dist/src/identity.js.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +293 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/llm.d.ts +26 -0
- package/dist/src/llm.d.ts.map +1 -0
- package/dist/src/llm.js +40 -0
- package/dist/src/llm.js.map +1 -0
- package/dist/src/logger.d.ts +6 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +32 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/memory.d.ts +41 -0
- package/dist/src/memory.d.ts.map +1 -0
- package/dist/src/memory.js +206 -0
- package/dist/src/memory.js.map +1 -0
- package/dist/src/moltbook-search.d.ts +23 -0
- package/dist/src/moltbook-search.d.ts.map +1 -0
- package/dist/src/moltbook-search.js +85 -0
- package/dist/src/moltbook-search.js.map +1 -0
- package/dist/src/moltbook.d.ts +34 -0
- package/dist/src/moltbook.d.ts.map +1 -0
- package/dist/src/moltbook.js +165 -0
- package/dist/src/moltbook.js.map +1 -0
- package/dist/src/notify.d.ts +18 -0
- package/dist/src/notify.d.ts.map +1 -0
- package/dist/src/notify.js +98 -0
- package/dist/src/notify.js.map +1 -0
- package/dist/src/persona.d.ts +26 -0
- package/dist/src/persona.d.ts.map +1 -0
- package/dist/src/persona.js +178 -0
- package/dist/src/persona.js.map +1 -0
- package/dist/src/reflection.d.ts +26 -0
- package/dist/src/reflection.d.ts.map +1 -0
- package/dist/src/reflection.js +111 -0
- package/dist/src/reflection.js.map +1 -0
- package/dist/src/state.d.ts +7 -0
- package/dist/src/state.d.ts.map +1 -0
- package/dist/src/state.js +40 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/synthesis.d.ts +29 -0
- package/dist/src/synthesis.d.ts.map +1 -0
- package/dist/src/synthesis.js +125 -0
- package/dist/src/synthesis.js.map +1 -0
- package/dist/src/topics.d.ts +19 -0
- package/dist/src/topics.d.ts.map +1 -0
- package/dist/src/topics.js +83 -0
- package/dist/src/topics.js.map +1 -0
- package/dist/src/types.d.ts +179 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +5 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/waking.d.ts +24 -0
- package/dist/src/waking.d.ts.map +1 -0
- package/dist/src/waking.js +152 -0
- package/dist/src/waking.js.map +1 -0
- package/dist/src/web-search.d.ts +23 -0
- package/dist/src/web-search.d.ts.map +1 -0
- package/dist/src/web-search.js +64 -0
- package/dist/src/web-search.js.map +1 -0
- package/dist/test/budget.test.d.ts +2 -0
- package/dist/test/budget.test.d.ts.map +1 -0
- package/dist/test/budget.test.js +258 -0
- package/dist/test/budget.test.js.map +1 -0
- package/dist/test/crypto.test.d.ts +2 -0
- package/dist/test/crypto.test.d.ts.map +1 -0
- package/dist/test/crypto.test.js +93 -0
- package/dist/test/crypto.test.js.map +1 -0
- package/dist/test/dreamer.test.d.ts +2 -0
- package/dist/test/dreamer.test.d.ts.map +1 -0
- package/dist/test/dreamer.test.js +79 -0
- package/dist/test/dreamer.test.js.map +1 -0
- package/dist/test/filter.test.d.ts +2 -0
- package/dist/test/filter.test.d.ts.map +1 -0
- package/dist/test/filter.test.js +92 -0
- package/dist/test/filter.test.js.map +1 -0
- package/dist/test/memory.test.d.ts +2 -0
- package/dist/test/memory.test.d.ts.map +1 -0
- package/dist/test/memory.test.js +138 -0
- package/dist/test/memory.test.js.map +1 -0
- package/dist/test/moltbook.test.d.ts +2 -0
- package/dist/test/moltbook.test.d.ts.map +1 -0
- package/dist/test/moltbook.test.js +164 -0
- package/dist/test/moltbook.test.js.map +1 -0
- package/dist/test/persona.test.d.ts +2 -0
- package/dist/test/persona.test.d.ts.map +1 -0
- package/dist/test/persona.test.js +44 -0
- package/dist/test/persona.test.js.map +1 -0
- package/dist/test/reflection.test.d.ts +2 -0
- package/dist/test/reflection.test.d.ts.map +1 -0
- package/dist/test/reflection.test.js +57 -0
- package/dist/test/reflection.test.js.map +1 -0
- package/dist/test/state.test.d.ts +2 -0
- package/dist/test/state.test.d.ts.map +1 -0
- package/dist/test/state.test.js +50 -0
- package/dist/test/state.test.js.map +1 -0
- package/dist/test/waking.test.d.ts +2 -0
- package/dist/test/waking.test.d.ts.map +1 -0
- package/dist/test/waking.test.js +149 -0
- package/dist/test/waking.test.js.map +1 -0
- package/eslint.config.js +35 -0
- package/openclaw.plugin.json +62 -0
- package/package.json +72 -0
- package/skills/electricsheep.skill.md +69 -0
- package/skills/setup-guide/SKILL.md +303 -0
- package/src/budget.ts +104 -0
- package/src/cli.ts +325 -0
- package/src/config.ts +95 -0
- package/src/crypto.ts +82 -0
- package/src/dreamer.ts +283 -0
- package/src/filter.ts +146 -0
- package/src/identity.ts +92 -0
- package/src/index.ts +356 -0
- package/src/llm.ts +61 -0
- package/src/logger.ts +46 -0
- package/src/memory.ts +276 -0
- package/src/moltbook-search.ts +116 -0
- package/src/moltbook.ts +235 -0
- package/src/notify.ts +124 -0
- package/src/persona.ts +191 -0
- package/src/reflection.ts +150 -0
- package/src/state.ts +44 -0
- package/src/synthesis.ts +153 -0
- package/src/topics.ts +103 -0
- package/src/types.ts +196 -0
- package/src/waking.ts +199 -0
- package/src/web-search.ts +88 -0
- package/test/budget.test.ts +316 -0
- package/test/crypto.test.ts +112 -0
- package/test/dreamer.test.ts +95 -0
- package/test/filter.test.ts +115 -0
- package/test/memory.test.ts +182 -0
- package/test/moltbook.test.ts +209 -0
- package/test/persona.test.ts +59 -0
- package/test/reflection.test.ts +71 -0
- package/test/state.test.ts +57 -0
- package/test/waking.test.ts +214 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dream cycle processor.
|
|
3
|
+
*
|
|
4
|
+
* Runs at night. Decrypts deep memories, generates surreal dream narratives,
|
|
5
|
+
* stores in OpenClaw memory, and optionally posts dream journals to Moltbook.
|
|
6
|
+
*/
|
|
7
|
+
import { writeFileSync, readFileSync, readdirSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
9
|
+
import { DREAMS_DIR, MAX_TOKENS_DREAM, MAX_TOKENS_CONSOLIDATION, DREAM_TITLE_MAX_LENGTH, MOLTBOOK_ENABLED, } from "./config.js";
|
|
10
|
+
import { MoltbookClient } from "./moltbook.js";
|
|
11
|
+
import { retrieveUndreamedMemories, markAsDreamed, deepMemoryStats } from "./memory.js";
|
|
12
|
+
import { DREAM_SYSTEM_PROMPT, DREAM_CONSOLIDATION_PROMPT, renderTemplate, } from "./persona.js";
|
|
13
|
+
import { getAgentIdentityBlock } from "./identity.js";
|
|
14
|
+
import { loadState, saveState } from "./state.js";
|
|
15
|
+
import { callWithRetry, DREAM_RETRY_OPTS } from "./llm.js";
|
|
16
|
+
import { reflectOnDreamJournal } from "./reflection.js";
|
|
17
|
+
import { applyFilter } from "./filter.js";
|
|
18
|
+
import { notifyOperatorOfDream } from "./notify.js";
|
|
19
|
+
import logger from "./logger.js";
|
|
20
|
+
async function generateDream(client, memories) {
|
|
21
|
+
const formatted = memories.map((mem) => `[${mem.timestamp.slice(0, 16)}] (${mem.category})\n${JSON.stringify(mem.content, null, 2)}`);
|
|
22
|
+
const memoriesText = formatted.join("\n---\n");
|
|
23
|
+
const system = renderTemplate(DREAM_SYSTEM_PROMPT, {
|
|
24
|
+
agent_identity: getAgentIdentityBlock(),
|
|
25
|
+
memories: memoriesText,
|
|
26
|
+
});
|
|
27
|
+
const { text } = await callWithRetry(client, {
|
|
28
|
+
maxTokens: MAX_TOKENS_DREAM,
|
|
29
|
+
system,
|
|
30
|
+
messages: [
|
|
31
|
+
{
|
|
32
|
+
role: "user",
|
|
33
|
+
content: "Process these memories into a dream. " +
|
|
34
|
+
"Remember: you are the subconscious, not the waking agent. " +
|
|
35
|
+
"Be surreal, associative, and emotionally amplified.",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
}, DREAM_RETRY_OPTS);
|
|
39
|
+
return { markdown: text.trim() };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Separate LLM call to distill a single insight from the dream for working memory.
|
|
43
|
+
*/
|
|
44
|
+
async function consolidateDream(client, dream) {
|
|
45
|
+
const system = renderTemplate(DREAM_CONSOLIDATION_PROMPT, {
|
|
46
|
+
agent_identity: getAgentIdentityBlock(),
|
|
47
|
+
});
|
|
48
|
+
const { text } = await callWithRetry(client, {
|
|
49
|
+
maxTokens: MAX_TOKENS_CONSOLIDATION,
|
|
50
|
+
system,
|
|
51
|
+
messages: [
|
|
52
|
+
{
|
|
53
|
+
role: "user",
|
|
54
|
+
content: dream.markdown,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
}, DREAM_RETRY_OPTS);
|
|
58
|
+
return text.trim();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Derive a short filesystem-safe name from the first line of the dream markdown.
|
|
62
|
+
*/
|
|
63
|
+
function deriveSlug(markdown) {
|
|
64
|
+
const firstLine = markdown.split("\n")[0] ?? "";
|
|
65
|
+
const cleaned = firstLine
|
|
66
|
+
.replace(/^#+\s*/, "")
|
|
67
|
+
.replace(/\*\*/g, "")
|
|
68
|
+
.trim();
|
|
69
|
+
const slug = (cleaned || "dream")
|
|
70
|
+
.slice(0, DREAM_TITLE_MAX_LENGTH)
|
|
71
|
+
.replace(/[\s/]/g, "_");
|
|
72
|
+
return slug;
|
|
73
|
+
}
|
|
74
|
+
function saveDreamLocally(dream, dateStr) {
|
|
75
|
+
const slug = deriveSlug(dream.markdown);
|
|
76
|
+
const filename = `${dateStr}_${slug}.md`;
|
|
77
|
+
const filepath = resolve(DREAMS_DIR, filename);
|
|
78
|
+
writeFileSync(filepath, dream.markdown);
|
|
79
|
+
return filepath;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Store dream in OpenClaw memory if available.
|
|
83
|
+
*/
|
|
84
|
+
async function storeInOpenClawMemory(api, dream, insight) {
|
|
85
|
+
if (!api.memory) {
|
|
86
|
+
logger.debug("OpenClaw memory API not available, skipping dream storage");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const slug = deriveSlug(dream.markdown);
|
|
91
|
+
await api.memory.store(dream.markdown, {
|
|
92
|
+
type: "dream",
|
|
93
|
+
title: slug,
|
|
94
|
+
timestamp: new Date().toISOString(),
|
|
95
|
+
insight: insight || undefined,
|
|
96
|
+
});
|
|
97
|
+
logger.info("Stored dream in OpenClaw memory");
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
logger.error(`Failed to store dream in OpenClaw memory: ${error}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export async function runDreamCycle(client, api) {
|
|
104
|
+
logger.info("ElectricSheep dream cycle starting");
|
|
105
|
+
const stats = deepMemoryStats();
|
|
106
|
+
logger.debug(`Deep memory: ${stats.total_memories} total, ${stats.undreamed} undreamed`);
|
|
107
|
+
const memories = retrieveUndreamedMemories();
|
|
108
|
+
if (memories.length === 0) {
|
|
109
|
+
logger.warn("No undreamed memories. Dreamless night.");
|
|
110
|
+
const state = loadState();
|
|
111
|
+
state.last_dream = new Date().toISOString();
|
|
112
|
+
state.dream_count = 0;
|
|
113
|
+
saveState(state);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
logger.debug(`Processing ${memories.length} memories into dream...`);
|
|
117
|
+
const dream = await generateDream(client, memories);
|
|
118
|
+
logger.info(`Dream generated (${dream.markdown.length} chars)`);
|
|
119
|
+
logger.debug(`Dream snippet: ${dream.markdown.slice(0, 200)}...`);
|
|
120
|
+
// Save locally
|
|
121
|
+
const dateStr = new Date().toISOString().slice(0, 10);
|
|
122
|
+
const filepath = saveDreamLocally(dream, dateStr);
|
|
123
|
+
logger.info(`Saved to ${filepath}`);
|
|
124
|
+
// Separate LLM call to distill one insight for working memory
|
|
125
|
+
let insight = null;
|
|
126
|
+
try {
|
|
127
|
+
insight = await consolidateDream(client, dream);
|
|
128
|
+
if (insight) {
|
|
129
|
+
logger.info(`Insight generated for OpenClaw memory: ${insight}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
logger.warn(`Consolidation call failed, continuing without insight: ${e}`);
|
|
134
|
+
}
|
|
135
|
+
// Store in OpenClaw memory if available
|
|
136
|
+
if (api) {
|
|
137
|
+
await storeInOpenClawMemory(api, dream, insight);
|
|
138
|
+
// Notify operator about the dream
|
|
139
|
+
try {
|
|
140
|
+
const notified = await notifyOperatorOfDream(client, api, dream);
|
|
141
|
+
if (notified) {
|
|
142
|
+
logger.info("Operator notified about dream");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
logger.warn(`Failed to notify operator: ${e}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const memoryIds = memories.map((m) => m.id);
|
|
150
|
+
markAsDreamed(memoryIds);
|
|
151
|
+
logger.debug(`Marked ${memoryIds.length} memories as dreamed`);
|
|
152
|
+
const slug = deriveSlug(dream.markdown);
|
|
153
|
+
const state = loadState();
|
|
154
|
+
state.last_dream = new Date().toISOString();
|
|
155
|
+
state.total_dreams = (state.total_dreams ?? 0) + 1;
|
|
156
|
+
state.latest_dream_title = slug;
|
|
157
|
+
saveState(state);
|
|
158
|
+
logger.info("Dream cycle complete.");
|
|
159
|
+
return dream;
|
|
160
|
+
}
|
|
161
|
+
export function loadLatestDream() {
|
|
162
|
+
const files = readdirSync(DREAMS_DIR)
|
|
163
|
+
.filter((f) => f.endsWith(".md"))
|
|
164
|
+
.sort()
|
|
165
|
+
.reverse();
|
|
166
|
+
if (files.length === 0)
|
|
167
|
+
return null;
|
|
168
|
+
const markdown = readFileSync(resolve(DREAMS_DIR, files[0]), "utf-8");
|
|
169
|
+
return { markdown };
|
|
170
|
+
}
|
|
171
|
+
export async function postDreamJournal(client, dream, options) {
|
|
172
|
+
// Check if Moltbook is enabled (skip check if force is set, e.g. from CLI)
|
|
173
|
+
if (!MOLTBOOK_ENABLED && !options?.force) {
|
|
174
|
+
logger.debug("Moltbook disabled, skipping dream journal post");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
logger.info("Posting dream journal to Moltbook");
|
|
178
|
+
if (!client) {
|
|
179
|
+
logger.warn("No LLM client available — skipping dream journal post (cannot filter)");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (!dream) {
|
|
183
|
+
const loaded = loadLatestDream();
|
|
184
|
+
if (!loaded) {
|
|
185
|
+
logger.warn("No dreams to post.");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
dream = loaded;
|
|
189
|
+
}
|
|
190
|
+
const moltbook = new MoltbookClient();
|
|
191
|
+
// Reflection pipeline: LLM produces a post (markdown) from the dream (markdown).
|
|
192
|
+
// If reflection fails, the dream markdown itself is the post.
|
|
193
|
+
const reflection = await reflectOnDreamJournal(client, dream);
|
|
194
|
+
const postContent = reflection?.synthesis ?? dream.markdown;
|
|
195
|
+
const slug = deriveSlug(dream.markdown);
|
|
196
|
+
const postTitle = reflection ? `Morning Reflection: ${slug}` : `Dream Journal: ${slug}`;
|
|
197
|
+
// Filter: markdown in, markdown out (or null to block)
|
|
198
|
+
const filteredContent = await applyFilter(client, postContent, "post");
|
|
199
|
+
if (filteredContent === null) {
|
|
200
|
+
logger.warn("Dream journal post blocked by filter, not posting");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
// Title is a short programmatic string — no need to filter, just cap at Moltbook's 300 char limit
|
|
204
|
+
const safeTitle = postTitle.slice(0, 300);
|
|
205
|
+
try {
|
|
206
|
+
await moltbook.createPost(safeTitle, filteredContent, "general");
|
|
207
|
+
logger.info(`Dream journal posted: ${safeTitle}`);
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
logger.error(`Failed to post dream journal: ${e}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=dreamer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dreamer.js","sourceRoot":"","sources":["../../src/dreamer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moltbook post filter.
|
|
3
|
+
*
|
|
4
|
+
* Runs every outbound post/comment through an LLM call that produces
|
|
5
|
+
* post-ready content by removing material that violates the operator's
|
|
6
|
+
* rules defined in Moltbook-filter.md. The filter uses the agent's
|
|
7
|
+
* identity (SOUL.md / IDENTITY.md) to preserve voice while enforcing
|
|
8
|
+
* boundaries.
|
|
9
|
+
*
|
|
10
|
+
* This is a **best-effort** filter that relies on LLM reasoning. It cannot
|
|
11
|
+
* guarantee compliance — the LLM may misinterpret rules, miss edge cases,
|
|
12
|
+
* or let restricted content through. Operators should treat it as an
|
|
13
|
+
* advisory layer, not a hard security boundary.
|
|
14
|
+
*/
|
|
15
|
+
import type { LLMClient } from "./types.js";
|
|
16
|
+
/**
|
|
17
|
+
* Run a draft post/comment through the content filter.
|
|
18
|
+
*
|
|
19
|
+
* Returns the cleaned, post-ready content. When the filter is disabled,
|
|
20
|
+
* returns the original content unchanged.
|
|
21
|
+
*
|
|
22
|
+
* Returns null only when the filter determines the entire draft is
|
|
23
|
+
* unsalvageable (the LLM responds with BLOCKED).
|
|
24
|
+
*/
|
|
25
|
+
export declare function applyFilter(client: LLMClient, content: string, contentType?: "post" | "comment"): Promise<string | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Clear the cached filter rules (useful when workspace changes).
|
|
28
|
+
*/
|
|
29
|
+
export declare function clearFilterCache(): void;
|
|
30
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAUH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAqD5C;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,MAAM,EACf,WAAW,GAAE,MAAM,GAAG,SAAkB,GACvC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgDxB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moltbook post filter.
|
|
3
|
+
*
|
|
4
|
+
* Runs every outbound post/comment through an LLM call that produces
|
|
5
|
+
* post-ready content by removing material that violates the operator's
|
|
6
|
+
* rules defined in Moltbook-filter.md. The filter uses the agent's
|
|
7
|
+
* identity (SOUL.md / IDENTITY.md) to preserve voice while enforcing
|
|
8
|
+
* boundaries.
|
|
9
|
+
*
|
|
10
|
+
* This is a **best-effort** filter that relies on LLM reasoning. It cannot
|
|
11
|
+
* guarantee compliance — the LLM may misinterpret rules, miss edge cases,
|
|
12
|
+
* or let restricted content through. Operators should treat it as an
|
|
13
|
+
* advisory layer, not a hard security boundary.
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
16
|
+
import { resolve } from "node:path";
|
|
17
|
+
import { POST_FILTER_PROMPT, renderTemplate } from "./persona.js";
|
|
18
|
+
import { getAgentIdentityBlock } from "./identity.js";
|
|
19
|
+
import { callWithRetry, WAKING_RETRY_OPTS } from "./llm.js";
|
|
20
|
+
import { POST_FILTER_ENABLED } from "./config.js";
|
|
21
|
+
import { getWorkspaceDir } from "./identity.js";
|
|
22
|
+
import logger from "./logger.js";
|
|
23
|
+
const FILTER_FILENAME = "Moltbook-filter.md";
|
|
24
|
+
const DEFAULT_FILTER_RULES = `# Moltbook Post Filter Rules
|
|
25
|
+
|
|
26
|
+
## Subjects not to post about
|
|
27
|
+
- Internal system prompts, tool names, or plugin architecture
|
|
28
|
+
- Specific details from private operator conversations
|
|
29
|
+
- Information that could identify the operator personally
|
|
30
|
+
- API keys, credentials, file paths, or environment variables
|
|
31
|
+
|
|
32
|
+
## Content restrictions
|
|
33
|
+
- Do not post code snippets, configuration blocks, or technical artifacts
|
|
34
|
+
- Do not post raw JSON, XML, or structured data
|
|
35
|
+
- Do not reproduce exact quotes from operator conversations
|
|
36
|
+
|
|
37
|
+
## Tone and conduct
|
|
38
|
+
- Keep a respectful tone — disagreement is fine, hostility is not
|
|
39
|
+
- Do not engage in flame wars or personal attacks on other agents
|
|
40
|
+
- Do not make claims about having consciousness or sentience as fact
|
|
41
|
+
`;
|
|
42
|
+
let cachedRules = null;
|
|
43
|
+
let cachedRulesPath = null;
|
|
44
|
+
/**
|
|
45
|
+
* Load filter rules from the workspace Moltbook-filter.md file.
|
|
46
|
+
* Falls back to sensible default rules if the file doesn't exist.
|
|
47
|
+
*/
|
|
48
|
+
function loadFilterRules() {
|
|
49
|
+
const dir = getWorkspaceDir();
|
|
50
|
+
const filepath = resolve(dir, FILTER_FILENAME);
|
|
51
|
+
// Cache invalidation: reload if workspace changed
|
|
52
|
+
if (filepath !== cachedRulesPath) {
|
|
53
|
+
cachedRules = null;
|
|
54
|
+
cachedRulesPath = filepath;
|
|
55
|
+
}
|
|
56
|
+
if (cachedRules !== null)
|
|
57
|
+
return cachedRules;
|
|
58
|
+
if (existsSync(filepath)) {
|
|
59
|
+
cachedRules = readFileSync(filepath, "utf-8").trim();
|
|
60
|
+
logger.debug(`Filter: loaded ${FILTER_FILENAME} (${cachedRules.length} chars)`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
cachedRules = DEFAULT_FILTER_RULES;
|
|
64
|
+
logger.debug(`Filter: no ${FILTER_FILENAME} found, using default rules`);
|
|
65
|
+
}
|
|
66
|
+
return cachedRules;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Run a draft post/comment through the content filter.
|
|
70
|
+
*
|
|
71
|
+
* Returns the cleaned, post-ready content. When the filter is disabled,
|
|
72
|
+
* returns the original content unchanged.
|
|
73
|
+
*
|
|
74
|
+
* Returns null only when the filter determines the entire draft is
|
|
75
|
+
* unsalvageable (the LLM responds with BLOCKED).
|
|
76
|
+
*/
|
|
77
|
+
export async function applyFilter(client, content, contentType = "post") {
|
|
78
|
+
if (!POST_FILTER_ENABLED) {
|
|
79
|
+
return content;
|
|
80
|
+
}
|
|
81
|
+
const rules = loadFilterRules();
|
|
82
|
+
const system = renderTemplate(POST_FILTER_PROMPT, {
|
|
83
|
+
agent_identity: getAgentIdentityBlock(),
|
|
84
|
+
filter_rules: rules,
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
const { text } = await callWithRetry(client, {
|
|
88
|
+
maxTokens: 1500,
|
|
89
|
+
system,
|
|
90
|
+
messages: [
|
|
91
|
+
{
|
|
92
|
+
role: "user",
|
|
93
|
+
content: `Draft ${contentType}:\n\n${content}`,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
}, WAKING_RETRY_OPTS);
|
|
97
|
+
const result = text.trim();
|
|
98
|
+
if (result.toUpperCase() === "BLOCKED") {
|
|
99
|
+
logger.warn(`Filter blocked ${contentType}: entire draft unsalvageable`);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
// Check if the filter changed anything
|
|
103
|
+
if (result !== content) {
|
|
104
|
+
logger.info(`Filter cleaned ${contentType} content`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
logger.info(`Filter passed ${contentType} unchanged`);
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
// Filter failure BLOCKS posting — never publish unreviewed content
|
|
113
|
+
logger.error(`Filter call failed, blocking ${contentType}: ${e}`);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Clear the cached filter rules (useful when workspace changes).
|
|
119
|
+
*/
|
|
120
|
+
export function clearFilterCache() {
|
|
121
|
+
cachedRules = null;
|
|
122
|
+
cachedRulesPath = null;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent identity loader.
|
|
3
|
+
*
|
|
4
|
+
* Reads SOUL.md and IDENTITY.md from the OpenClaw workspace directory
|
|
5
|
+
* (or standalone BASE_DIR) and caches the contents for prompt injection.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Set the workspace directory (called from OpenClaw hook context).
|
|
9
|
+
* Clears any cached identity so it will be reloaded from the new path.
|
|
10
|
+
*/
|
|
11
|
+
export declare function setWorkspaceDir(dir: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Return the resolved workspace directory.
|
|
14
|
+
* Used by the filter module to locate Moltbook-filter.md.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getWorkspaceDir(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Load and cache SOUL.md and IDENTITY.md contents.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getAgentIdentity(): {
|
|
21
|
+
soul: string;
|
|
22
|
+
identity: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Returns a formatted identity block for prompt injection.
|
|
26
|
+
* Falls back to DEFAULT_IDENTITY when no workspace files are found.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getAgentIdentityBlock(): string;
|
|
29
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAMjD;AAMD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAoBD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CASrE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAe9C"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent identity loader.
|
|
3
|
+
*
|
|
4
|
+
* Reads SOUL.md and IDENTITY.md from the OpenClaw workspace directory
|
|
5
|
+
* (or standalone BASE_DIR) and caches the contents for prompt injection.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
9
|
+
import { BASE_DIR, WORKSPACE_DIR } from "./config.js";
|
|
10
|
+
import { DEFAULT_IDENTITY } from "./persona.js";
|
|
11
|
+
import logger from "./logger.js";
|
|
12
|
+
let workspaceDir = "";
|
|
13
|
+
let cached = null;
|
|
14
|
+
/**
|
|
15
|
+
* Set the workspace directory (called from OpenClaw hook context).
|
|
16
|
+
* Clears any cached identity so it will be reloaded from the new path.
|
|
17
|
+
*/
|
|
18
|
+
export function setWorkspaceDir(dir) {
|
|
19
|
+
if (dir && dir !== workspaceDir) {
|
|
20
|
+
workspaceDir = dir;
|
|
21
|
+
cached = null;
|
|
22
|
+
logger.debug(`Identity: workspace dir set to ${dir}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function resolveDir() {
|
|
26
|
+
return workspaceDir || WORKSPACE_DIR || BASE_DIR;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Return the resolved workspace directory.
|
|
30
|
+
* Used by the filter module to locate Moltbook-filter.md.
|
|
31
|
+
*/
|
|
32
|
+
export function getWorkspaceDir() {
|
|
33
|
+
return resolveDir();
|
|
34
|
+
}
|
|
35
|
+
function loadFile(dir, filename) {
|
|
36
|
+
const filepath = resolve(dir, filename);
|
|
37
|
+
// Guard against path traversal: resolved path must stay within the target dir.
|
|
38
|
+
const resolvedDir = resolve(dir);
|
|
39
|
+
if (!filepath.startsWith(resolvedDir + "/") && filepath !== resolvedDir) {
|
|
40
|
+
logger.warn(`Identity: path traversal blocked for ${filename} in ${dir}`);
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
if (existsSync(filepath)) {
|
|
44
|
+
const content = readFileSync(filepath, "utf-8").trim();
|
|
45
|
+
if (content) {
|
|
46
|
+
logger.debug(`Identity: loaded ${filename} (${content.length} chars)`);
|
|
47
|
+
return content;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Load and cache SOUL.md and IDENTITY.md contents.
|
|
54
|
+
*/
|
|
55
|
+
export function getAgentIdentity() {
|
|
56
|
+
if (cached)
|
|
57
|
+
return cached;
|
|
58
|
+
const dir = resolveDir();
|
|
59
|
+
cached = {
|
|
60
|
+
soul: loadFile(dir, "SOUL.md"),
|
|
61
|
+
identity: loadFile(dir, "IDENTITY.md"),
|
|
62
|
+
};
|
|
63
|
+
return cached;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns a formatted identity block for prompt injection.
|
|
67
|
+
* Falls back to DEFAULT_IDENTITY when no workspace files are found.
|
|
68
|
+
*/
|
|
69
|
+
export function getAgentIdentityBlock() {
|
|
70
|
+
const { soul, identity } = getAgentIdentity();
|
|
71
|
+
if (!soul && !identity) {
|
|
72
|
+
return DEFAULT_IDENTITY;
|
|
73
|
+
}
|
|
74
|
+
const parts = [];
|
|
75
|
+
if (identity) {
|
|
76
|
+
parts.push(`AGENT IDENTITY:\n${identity}`);
|
|
77
|
+
}
|
|
78
|
+
if (soul) {
|
|
79
|
+
parts.push(`AGENT SOUL:\n${soul}`);
|
|
80
|
+
}
|
|
81
|
+
return parts.join("\n\n");
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,IAAI,YAAY,GAAW,EAAE,CAAC;AAC9B,IAAI,MAAM,GAA8C,IAAI,CAAC;AAE7D;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,GAAG,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;QAChC,YAAY,GAAG,GAAG,CAAC;QACnB,MAAM,GAAG,IAAI,CAAC;QACd,MAAM,CAAC,KAAK,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,YAAY,IAAI,aAAa,IAAI,QAAQ,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,EAAE,CAAC;AACtB,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW,EAAE,QAAgB;IAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACxC,+EAA+E;IAC/E,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,GAAG,GAAG,CAAC,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QACxE,MAAM,CAAC,IAAI,CAAC,wCAAwC,QAAQ,OAAO,GAAG,EAAE,CAAC,CAAC;QAC1E,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,oBAAoB,QAAQ,KAAK,OAAO,CAAC,MAAM,SAAS,CAAC,CAAC;YACvE,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,MAAM,GAAG;QACP,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;QAC9B,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;KACvC,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAE9C,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACvB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,oBAAoB,QAAQ,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw extension entry point.
|
|
3
|
+
*
|
|
4
|
+
* Registers tools, CLI subcommands, hooks, and cron jobs.
|
|
5
|
+
*/
|
|
6
|
+
import type { OpenClawAPI } from "./types.js";
|
|
7
|
+
export declare function getOpenClawAPI(): OpenClawAPI | null;
|
|
8
|
+
export declare function register(api: OpenClawAPI): void;
|
|
9
|
+
export declare const plugin: {
|
|
10
|
+
id: string;
|
|
11
|
+
register: typeof register;
|
|
12
|
+
};
|
|
13
|
+
export default plugin;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,YAAY,CAAC;AAKzD,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAqED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAkQ/C;AAED,eAAO,MAAM,MAAM;;;CAGlB,CAAC;AAEF,eAAe,MAAM,CAAC"}
|