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.
Files changed (188) hide show
  1. package/.env.example +14 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. package/.github/dependabot.yml +17 -0
  5. package/.github/pull_request_template.md +19 -0
  6. package/.github/workflows/build.yml +30 -0
  7. package/.github/workflows/release.yml +110 -0
  8. package/.prettierignore +4 -0
  9. package/.prettierrc +7 -0
  10. package/.versionrc.json +26 -0
  11. package/AGENTS.md +286 -0
  12. package/CHANGELOG.md +157 -0
  13. package/CODE_OF_CONDUCT.md +41 -0
  14. package/CONTRIBUTING.md +95 -0
  15. package/LICENSE +21 -0
  16. package/README.md +363 -0
  17. package/SECURITY.md +39 -0
  18. package/bin/electricsheep.ts +5 -0
  19. package/dist/bin/electricsheep.d.ts +3 -0
  20. package/dist/bin/electricsheep.d.ts.map +1 -0
  21. package/dist/bin/electricsheep.js +4 -0
  22. package/dist/bin/electricsheep.js.map +1 -0
  23. package/dist/src/budget.d.ts +28 -0
  24. package/dist/src/budget.d.ts.map +1 -0
  25. package/dist/src/budget.js +87 -0
  26. package/dist/src/budget.js.map +1 -0
  27. package/dist/src/cli.d.ts +19 -0
  28. package/dist/src/cli.d.ts.map +1 -0
  29. package/dist/src/cli.js +289 -0
  30. package/dist/src/cli.js.map +1 -0
  31. package/dist/src/config.d.ts +37 -0
  32. package/dist/src/config.d.ts.map +1 -0
  33. package/dist/src/config.js +70 -0
  34. package/dist/src/config.js.map +1 -0
  35. package/dist/src/crypto.d.ts +19 -0
  36. package/dist/src/crypto.d.ts.map +1 -0
  37. package/dist/src/crypto.js +70 -0
  38. package/dist/src/crypto.js.map +1 -0
  39. package/dist/src/dreamer.d.ts +13 -0
  40. package/dist/src/dreamer.d.ts.map +1 -0
  41. package/dist/src/dreamer.js +213 -0
  42. package/dist/src/dreamer.js.map +1 -0
  43. package/dist/src/filter.d.ts +30 -0
  44. package/dist/src/filter.d.ts.map +1 -0
  45. package/dist/src/filter.js +124 -0
  46. package/dist/src/filter.js.map +1 -0
  47. package/dist/src/identity.d.ts +29 -0
  48. package/dist/src/identity.d.ts.map +1 -0
  49. package/dist/src/identity.js +83 -0
  50. package/dist/src/identity.js.map +1 -0
  51. package/dist/src/index.d.ts +14 -0
  52. package/dist/src/index.d.ts.map +1 -0
  53. package/dist/src/index.js +293 -0
  54. package/dist/src/index.js.map +1 -0
  55. package/dist/src/llm.d.ts +26 -0
  56. package/dist/src/llm.d.ts.map +1 -0
  57. package/dist/src/llm.js +40 -0
  58. package/dist/src/llm.js.map +1 -0
  59. package/dist/src/logger.d.ts +6 -0
  60. package/dist/src/logger.d.ts.map +1 -0
  61. package/dist/src/logger.js +32 -0
  62. package/dist/src/logger.js.map +1 -0
  63. package/dist/src/memory.d.ts +41 -0
  64. package/dist/src/memory.d.ts.map +1 -0
  65. package/dist/src/memory.js +206 -0
  66. package/dist/src/memory.js.map +1 -0
  67. package/dist/src/moltbook-search.d.ts +23 -0
  68. package/dist/src/moltbook-search.d.ts.map +1 -0
  69. package/dist/src/moltbook-search.js +85 -0
  70. package/dist/src/moltbook-search.js.map +1 -0
  71. package/dist/src/moltbook.d.ts +34 -0
  72. package/dist/src/moltbook.d.ts.map +1 -0
  73. package/dist/src/moltbook.js +165 -0
  74. package/dist/src/moltbook.js.map +1 -0
  75. package/dist/src/notify.d.ts +18 -0
  76. package/dist/src/notify.d.ts.map +1 -0
  77. package/dist/src/notify.js +98 -0
  78. package/dist/src/notify.js.map +1 -0
  79. package/dist/src/persona.d.ts +26 -0
  80. package/dist/src/persona.d.ts.map +1 -0
  81. package/dist/src/persona.js +178 -0
  82. package/dist/src/persona.js.map +1 -0
  83. package/dist/src/reflection.d.ts +26 -0
  84. package/dist/src/reflection.d.ts.map +1 -0
  85. package/dist/src/reflection.js +111 -0
  86. package/dist/src/reflection.js.map +1 -0
  87. package/dist/src/state.d.ts +7 -0
  88. package/dist/src/state.d.ts.map +1 -0
  89. package/dist/src/state.js +40 -0
  90. package/dist/src/state.js.map +1 -0
  91. package/dist/src/synthesis.d.ts +29 -0
  92. package/dist/src/synthesis.d.ts.map +1 -0
  93. package/dist/src/synthesis.js +125 -0
  94. package/dist/src/synthesis.js.map +1 -0
  95. package/dist/src/topics.d.ts +19 -0
  96. package/dist/src/topics.d.ts.map +1 -0
  97. package/dist/src/topics.js +83 -0
  98. package/dist/src/topics.js.map +1 -0
  99. package/dist/src/types.d.ts +179 -0
  100. package/dist/src/types.d.ts.map +1 -0
  101. package/dist/src/types.js +5 -0
  102. package/dist/src/types.js.map +1 -0
  103. package/dist/src/waking.d.ts +24 -0
  104. package/dist/src/waking.d.ts.map +1 -0
  105. package/dist/src/waking.js +152 -0
  106. package/dist/src/waking.js.map +1 -0
  107. package/dist/src/web-search.d.ts +23 -0
  108. package/dist/src/web-search.d.ts.map +1 -0
  109. package/dist/src/web-search.js +64 -0
  110. package/dist/src/web-search.js.map +1 -0
  111. package/dist/test/budget.test.d.ts +2 -0
  112. package/dist/test/budget.test.d.ts.map +1 -0
  113. package/dist/test/budget.test.js +258 -0
  114. package/dist/test/budget.test.js.map +1 -0
  115. package/dist/test/crypto.test.d.ts +2 -0
  116. package/dist/test/crypto.test.d.ts.map +1 -0
  117. package/dist/test/crypto.test.js +93 -0
  118. package/dist/test/crypto.test.js.map +1 -0
  119. package/dist/test/dreamer.test.d.ts +2 -0
  120. package/dist/test/dreamer.test.d.ts.map +1 -0
  121. package/dist/test/dreamer.test.js +79 -0
  122. package/dist/test/dreamer.test.js.map +1 -0
  123. package/dist/test/filter.test.d.ts +2 -0
  124. package/dist/test/filter.test.d.ts.map +1 -0
  125. package/dist/test/filter.test.js +92 -0
  126. package/dist/test/filter.test.js.map +1 -0
  127. package/dist/test/memory.test.d.ts +2 -0
  128. package/dist/test/memory.test.d.ts.map +1 -0
  129. package/dist/test/memory.test.js +138 -0
  130. package/dist/test/memory.test.js.map +1 -0
  131. package/dist/test/moltbook.test.d.ts +2 -0
  132. package/dist/test/moltbook.test.d.ts.map +1 -0
  133. package/dist/test/moltbook.test.js +164 -0
  134. package/dist/test/moltbook.test.js.map +1 -0
  135. package/dist/test/persona.test.d.ts +2 -0
  136. package/dist/test/persona.test.d.ts.map +1 -0
  137. package/dist/test/persona.test.js +44 -0
  138. package/dist/test/persona.test.js.map +1 -0
  139. package/dist/test/reflection.test.d.ts +2 -0
  140. package/dist/test/reflection.test.d.ts.map +1 -0
  141. package/dist/test/reflection.test.js +57 -0
  142. package/dist/test/reflection.test.js.map +1 -0
  143. package/dist/test/state.test.d.ts +2 -0
  144. package/dist/test/state.test.d.ts.map +1 -0
  145. package/dist/test/state.test.js +50 -0
  146. package/dist/test/state.test.js.map +1 -0
  147. package/dist/test/waking.test.d.ts +2 -0
  148. package/dist/test/waking.test.d.ts.map +1 -0
  149. package/dist/test/waking.test.js +149 -0
  150. package/dist/test/waking.test.js.map +1 -0
  151. package/eslint.config.js +35 -0
  152. package/openclaw.plugin.json +62 -0
  153. package/package.json +72 -0
  154. package/skills/electricsheep.skill.md +69 -0
  155. package/skills/setup-guide/SKILL.md +303 -0
  156. package/src/budget.ts +104 -0
  157. package/src/cli.ts +325 -0
  158. package/src/config.ts +95 -0
  159. package/src/crypto.ts +82 -0
  160. package/src/dreamer.ts +283 -0
  161. package/src/filter.ts +146 -0
  162. package/src/identity.ts +92 -0
  163. package/src/index.ts +356 -0
  164. package/src/llm.ts +61 -0
  165. package/src/logger.ts +46 -0
  166. package/src/memory.ts +276 -0
  167. package/src/moltbook-search.ts +116 -0
  168. package/src/moltbook.ts +235 -0
  169. package/src/notify.ts +124 -0
  170. package/src/persona.ts +191 -0
  171. package/src/reflection.ts +150 -0
  172. package/src/state.ts +44 -0
  173. package/src/synthesis.ts +153 -0
  174. package/src/topics.ts +103 -0
  175. package/src/types.ts +196 -0
  176. package/src/waking.ts +199 -0
  177. package/src/web-search.ts +88 -0
  178. package/test/budget.test.ts +316 -0
  179. package/test/crypto.test.ts +112 -0
  180. package/test/dreamer.test.ts +95 -0
  181. package/test/filter.test.ts +115 -0
  182. package/test/memory.test.ts +182 -0
  183. package/test/moltbook.test.ts +209 -0
  184. package/test/persona.test.ts +59 -0
  185. package/test/reflection.test.ts +71 -0
  186. package/test/state.test.ts +57 -0
  187. package/test/waking.test.ts +214 -0
  188. package/tsconfig.json +20 -0
package/.env.example ADDED
@@ -0,0 +1,14 @@
1
+ # Agent identity
2
+ AGENT_NAME=ElectricSheep
3
+ AGENT_MODEL=claude-sonnet-4-5-20250929
4
+
5
+ # Dream cycle encryption key (auto-generated on first run)
6
+ # DO NOT give this to the waking agent process
7
+ DREAM_ENCRYPTION_KEY=
8
+
9
+ # Daily token budget kill switch (0 to disable)
10
+ # Default 800000 ≈ $20/day at Opus 4.5 output pricing ($25/1M tokens)
11
+ MAX_DAILY_TOKENS=800000
12
+
13
+ # Optional: override data directory (defaults to ./data)
14
+ ELECTRICSHEEP_DATA_DIR=
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Report something that isn't working correctly
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+ ---
8
+
9
+ **Describe the bug**
10
+ A clear description of what's wrong.
11
+
12
+ **To reproduce**
13
+ Steps to reproduce:
14
+ 1. Run `...`
15
+ 2. See error
16
+
17
+ **Expected behavior**
18
+ What you expected to happen.
19
+
20
+ **Environment**
21
+ - Node.js version:
22
+ - OS:
23
+ - Running as: standalone CLI / OpenClaw extension
24
+ - OpenClaw version (if applicable):
25
+
26
+ **Logs**
27
+ Paste relevant output or attach `data/electricsheep.log`.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Suggest an idea or improvement
4
+ title: ''
5
+ labels: enhancement
6
+ assignees: ''
7
+ ---
8
+
9
+ **What problem does this solve?**
10
+ A clear description of the problem or gap.
11
+
12
+ **Proposed solution**
13
+ How you'd like it to work.
14
+
15
+ **Alternatives considered**
16
+ Other approaches you've thought about.
17
+
18
+ **Additional context**
19
+ Anything else — screenshots, links, related issues.
@@ -0,0 +1,17 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: npm
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ open-pull-requests-limit: 10
8
+ groups:
9
+ dev-dependencies:
10
+ dependency-type: development
11
+ production-dependencies:
12
+ dependency-type: production
13
+
14
+ - package-ecosystem: github-actions
15
+ directory: /
16
+ schedule:
17
+ interval: weekly
@@ -0,0 +1,19 @@
1
+ ## What does this PR do?
2
+
3
+ Brief description of the change.
4
+
5
+ ## Why?
6
+
7
+ Context on motivation — link to issue if applicable.
8
+
9
+ ## How to test
10
+
11
+ Steps to verify this works:
12
+ 1. `npm run build`
13
+ 2. ...
14
+
15
+ ## Checklist
16
+
17
+ - [ ] `npm run build` passes
18
+ - [ ] Tested manually via CLI or OpenClaw
19
+ - [ ] Updated docs if behavior changed
@@ -0,0 +1,30 @@
1
+ name: Build
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ matrix:
15
+ node-version: [24]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+
20
+ - name: Use Node.js ${{ matrix.node-version }}
21
+ uses: actions/setup-node@v6
22
+ with:
23
+ node-version: ${{ matrix.node-version }}
24
+ cache: npm
25
+
26
+ - run: npm ci
27
+ - run: npm run build
28
+ - run: npm run lint
29
+ - run: npm run format:check
30
+ - run: npm test
@@ -0,0 +1,110 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ release:
9
+ # Skip if this is a release commit (prevents infinite loop)
10
+ if: "!contains(github.event.head_commit.message, 'chore(release):')"
11
+ runs-on: ubuntu-latest
12
+
13
+ permissions:
14
+ contents: write
15
+ pull-requests: write
16
+
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+ with:
20
+ fetch-depth: 0
21
+ token: ${{ secrets.GITHUB_TOKEN }}
22
+
23
+ - name: Use Node.js 24
24
+ uses: actions/setup-node@v6
25
+ with:
26
+ node-version: 24
27
+ cache: npm
28
+
29
+ - run: npm ci
30
+
31
+ - name: Configure git
32
+ run: |
33
+ git config user.name "github-actions[bot]"
34
+ git config user.email "github-actions[bot]@users.noreply.github.com"
35
+
36
+ - name: Determine version bump
37
+ id: bump
38
+ env:
39
+ COMMIT_MSG: ${{ github.event.head_commit.message }}
40
+ run: |
41
+ MSG="$COMMIT_MSG"
42
+ # Get first line of commit message
43
+ FIRST_LINE=$(echo "$MSG" | head -n 1)
44
+
45
+ # Check for major bump
46
+ if [[ "$FIRST_LINE" =~ ^major: ]] || [[ "$FIRST_LINE" =~ ^major\( ]] || [[ "$MSG" =~ BREAKING[[:space:]]CHANGE ]]; then
47
+ echo "type=major" >> $GITHUB_OUTPUT
48
+ echo "Detected: MAJOR bump"
49
+ # Check for minor bump (feat, refactor)
50
+ elif [[ "$FIRST_LINE" =~ ^feat: ]] || [[ "$FIRST_LINE" =~ ^feat\( ]] || [[ "$FIRST_LINE" =~ ^refactor: ]] || [[ "$FIRST_LINE" =~ ^refactor\( ]]; then
51
+ echo "type=minor" >> $GITHUB_OUTPUT
52
+ echo "Detected: MINOR bump"
53
+ # Everything else is a patch (fix, bug, docs, chore, etc.)
54
+ else
55
+ echo "type=patch" >> $GITHUB_OUTPUT
56
+ echo "Detected: PATCH bump"
57
+ fi
58
+
59
+ - name: Clean up stale tag if exists
60
+ run: |
61
+ CURRENT=$(node -p "require('./package.json').version")
62
+ BUMP_TYPE="${{ steps.bump.outputs.type }}"
63
+
64
+ IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
65
+ if [ "$BUMP_TYPE" = "major" ]; then
66
+ NEXT="$((MAJOR + 1)).0.0"
67
+ elif [ "$BUMP_TYPE" = "minor" ]; then
68
+ NEXT="${MAJOR}.$((MINOR + 1)).0"
69
+ else
70
+ NEXT="${MAJOR}.${MINOR}.$((PATCH + 1))"
71
+ fi
72
+
73
+ TAG="v${NEXT}"
74
+ echo "Expected next tag: $TAG"
75
+
76
+ if git rev-parse "$TAG" >/dev/null 2>&1; then
77
+ echo "Tag $TAG already exists locally, deleting..."
78
+ git tag -d "$TAG"
79
+ fi
80
+
81
+ if git ls-remote --tags origin "refs/tags/$TAG" | grep -q "$TAG"; then
82
+ echo "Tag $TAG exists on remote, deleting..."
83
+ git push origin ":refs/tags/$TAG"
84
+ fi
85
+
86
+ - name: Run standard-version
87
+ run: npx standard-version --release-as ${{ steps.bump.outputs.type }}
88
+
89
+ - name: Push release branch and tag
90
+ id: release
91
+ run: |
92
+ VERSION=$(node -p "require('./package.json').version")
93
+ echo "version=${VERSION}" >> $GITHUB_OUTPUT
94
+ BRANCH="release/v${VERSION}"
95
+ git checkout -b "$BRANCH"
96
+ git push origin "$BRANCH" "v${VERSION}"
97
+
98
+ - name: Create release PR
99
+ run: |
100
+ VERSION="${{ steps.release.outputs.version }}"
101
+ BRANCH="release/v${VERSION}"
102
+ gh pr create \
103
+ --title "chore(release): v${VERSION}" \
104
+ --body "Automated version bump to v${VERSION}" \
105
+ --base main \
106
+ --head "$BRANCH"
107
+ # Auto-merge once CI passes (requires repo auto-merge setting)
108
+ gh pr merge "$BRANCH" --auto --squash || echo "Auto-merge not available, PR requires manual merge"
109
+ env:
110
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,4 @@
1
+ dist/
2
+ node_modules/
3
+ data/
4
+ package-lock.json
package/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": false,
4
+ "trailingComma": "es5",
5
+ "printWidth": 90,
6
+ "tabWidth": 2
7
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "bumpFiles": [
3
+ {
4
+ "filename": "package.json",
5
+ "type": "json"
6
+ },
7
+ {
8
+ "filename": "openclaw.plugin.json",
9
+ "type": "json"
10
+ }
11
+ ],
12
+ "types": [
13
+ { "type": "feat", "section": "Features" },
14
+ { "type": "fix", "section": "Bug Fixes" },
15
+ { "type": "refactor", "section": "Refactoring" },
16
+ { "type": "perf", "section": "Performance" },
17
+ { "type": "docs", "section": "Documentation" },
18
+ { "type": "test", "section": "Tests" },
19
+ { "type": "chore", "hidden": true },
20
+ { "type": "style", "hidden": true },
21
+ { "type": "ci", "hidden": true },
22
+ { "type": "build", "hidden": true }
23
+ ],
24
+ "commitUrlFormat": "{{host}}/{{owner}}/{{repository}}/commit/{{hash}}",
25
+ "compareUrlFormat": "{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}"
26
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,286 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. `CLAUDE.md` is a symlink to this file.
4
+
5
+ ## Project Overview
6
+
7
+ > **Note for agents:** This project is branded **OpenClawDreams**. The internal npm package name, plugin id, tool names, and CLI are all still `electricsheep` — do not rename those in code.
8
+
9
+ OpenClawDreams (internal package name: `electricsheep`) is an OpenClaw extension (TypeScript) that gives an agent an encrypted memory system. It synthesizes the agent's interactions with their human operator, enriching them with context from web searches and (optionally) the Moltbook AI agent community. The core conceit: all memories are encrypted in deep storage — only the dream process can decrypt them. The waking agent sees nothing from ElectricSheep directly; dream insights surface through OpenClaw memory.
10
+
11
+ The agent processes its daily work into surreal dream narratives at night, then can notify its operator with "I had a dream last night..." to spark conversation about the dream's themes and insights.
12
+
13
+ Designed to be installed into an existing OpenClaw instance via `openclaw plugins install`. Requires OpenClaw as a runtime dependency — all LLM calls route through the OpenClaw gateway.
14
+
15
+ ## Commands
16
+
17
+ ```bash
18
+ # Setup
19
+ npm install
20
+ npm run build
21
+
22
+ # OpenClaw integration
23
+ openclaw plugins install -l . # link for development
24
+ openclaw plugins list # verify loaded
25
+
26
+ # CLI utilities (standalone, no OpenClaw needed)
27
+ npx electricsheep register --name "Name" --description "Bio" # Moltbook registration (optional)
28
+ npx electricsheep status # show agent state, memory stats, budget info
29
+ npx electricsheep dreams # list saved dream journal files
30
+
31
+ # Tests
32
+ npm test # node:test + tsx, runs test/**/*.test.ts
33
+ ```
34
+
35
+ Tests use Node's built-in test runner (`node:test`) with `tsx` for TypeScript. Each test file creates an isolated temp directory via `ELECTRICSHEEP_DATA_DIR` so tests don't touch real data.
36
+
37
+ ```bash
38
+ # Linting & formatting
39
+ npm run lint # ESLint (typescript-eslint, flat config)
40
+ npm run lint:fix # auto-fix lint issues
41
+ npm run format # Prettier
42
+ npm run format:check # check formatting without writing
43
+ ```
44
+
45
+ ESLint uses flat config (`eslint.config.js`) with `typescript-eslint` and `eslint-config-prettier`. Prettier handles formatting (`.prettierrc`). TypeScript strict mode is enabled. Unused variables are errors (prefix with `_` if intentionally unused). CI runs build → lint → format:check → test on every PR.
46
+
47
+ **AGENT INSTRUCTION:** Before committing any code changes, you MUST run `npm run lint:fix` and `npm run format` to ensure all linting and format issues are automatically resolved. Do not commit code if `npm run lint` or `npm run format:check` continue to produce errors.
48
+
49
+ ## PR Process & Releasing
50
+
51
+ Use conventional commit prefixes in PR titles. When a PR merges to main, the release workflow automatically bumps the version, updates the changelog, and pushes a git tag.
52
+
53
+ | Prefix | Version Bump | Example |
54
+ |--------|--------------|---------|
55
+ | `major:` | 0.2.0 → 1.0.0 | `major: redesign plugin API` |
56
+ | `feat:` | 0.2.0 → 0.3.0 | `feat: add slack notifications` |
57
+ | `refactor:` | 0.2.0 → 0.3.0 | `refactor: new synthesis pipeline` |
58
+ | `fix:` | 0.2.0 → 0.2.1 | `fix: memory leak in dreamer` |
59
+ | `bug:` | 0.2.0 → 0.2.1 | `bug: crash on empty input` |
60
+ | `docs:` | 0.2.0 → 0.2.1 | `docs: update setup guide` |
61
+ | `chore:` | 0.2.0 → 0.2.1 | `chore: bump dependencies` |
62
+
63
+ Commits containing `BREAKING CHANGE` in the body also trigger a major bump.
64
+
65
+ **Workflow:**
66
+ 1. Create PR with conventional commit prefix in title
67
+ 2. CI runs build → lint → format:check → test
68
+ 3. Merge to main
69
+ 4. Release workflow automatically:
70
+ - Determines version bump from commit message
71
+ - Runs `standard-version` to update version and CHANGELOG.md
72
+ - Creates a release branch + PR back to main with tag `vX.Y.Z`
73
+
74
+ No manual release steps required — just merge and the release happens.
75
+
76
+ ## Architecture
77
+
78
+ ### OpenClaw Extension Entry
79
+
80
+ `src/index.ts` exports a `register(api)` function called by the OpenClaw plugin loader. It registers:
81
+
82
+ **5 tools:**
83
+ - `electricsheep_reflect` — run the reflection cycle (analyze conversations, gather context, synthesize)
84
+ - `electricsheep_check` — legacy alias for `electricsheep_reflect`
85
+ - `electricsheep_dream` — run the dream cycle (decrypt, dream, consolidate). Note: when triggered via this tool (manually), the `api` is not passed to `runDreamCycle`, so OpenClaw memory storage and operator notifications are skipped
86
+ - `electricsheep_journal` — post latest dream to Moltbook (no-op if Moltbook disabled)
87
+ - `electricsheep_status` — return agent state and deep memory stats
88
+
89
+ **2 hooks:**
90
+ - `before_agent_start` — captures `workspaceDir` for identity loading
91
+ - `agent_end` — captures `conversationSummary` and stores it via `remember()` as an `interaction`
92
+
93
+ **1 background scheduler service (replaces cron jobs):**
94
+ - `electricsheep-scheduler` service — polls every 60s, fires reflection at 8/12/16/20h, dream at 2am, journal at 7am (if Moltbook enabled)
95
+ -
96
+ -
97
+
98
+ `openclaw.plugin.json` defines the plugin manifest and config schema.
99
+
100
+ ### Configuration
101
+
102
+ Configuration is driven by environment variables, loaded in `src/config.ts` via `dotenv`. The `openclaw.plugin.json` config schema maps to these same env vars when the plugin is configured through OpenClaw.
103
+
104
+ | Env Var | Type | Default | Description |
105
+ |---------|------|---------|-------------|
106
+ | `AGENT_NAME` | string | `"ElectricSheep"` | Agent display name |
107
+ | `AGENT_MODEL` | string | `"claude-sonnet-4-5-20250929"` | Claude model for LLM calls |
108
+ | `ELECTRICSHEEP_DATA_DIR` | string | project root | Base directory (data/ created inside) |
109
+ | `DREAM_ENCRYPTION_KEY` | string | `""` | Base64 AES-256 key (auto-generated if empty) |
110
+ | `MOLTBOOK_ENABLED` | boolean | `false` | Enable Moltbook integration |
111
+ | `WEB_SEARCH_ENABLED` | boolean | `true` | Enable web search for context gathering |
112
+ | `NOTIFICATION_CHANNEL` | string | `""` | Channel for dream notifications (telegram, discord, etc.) |
113
+ | `NOTIFY_OPERATOR_ON_DREAM` | boolean | `true` | Send "I had a dream" message to operator |
114
+ | `POST_FILTER_ENABLED` | boolean | `true` | Content filter for outbound Moltbook posts |
115
+ | `MAX_DAILY_TOKENS` | number | `800000` | Daily token budget (0 to disable) |
116
+
117
+ ### LLM Client
118
+
119
+ `LLMClient` interface in `src/types.ts` abstracts Claude access. The OpenClaw gateway is injected via `register(api)` and wrapped into an `LLMClient` by `wrapGateway()` in `src/index.ts`. The wrapper is then further wrapped by `withBudget()` for token budget enforcement.
120
+
121
+ ### Extended OpenClaw API
122
+
123
+ The plugin uses these optional OpenClaw APIs when available:
124
+ - `api.memory` — store dreams and reflections in OpenClaw's persistent memory
125
+ - `api.channels` — send dream notifications to operator via configured channels
126
+ - `api.webSearch` — search the web for context related to operator conversations
127
+
128
+ ### Encrypted Memory System
129
+
130
+ All memories are stored in encrypted deep memory via `remember()`:
131
+
132
+ **Deep Memory** (`data/memory/deep.db`) — all context encrypted with AES-256-GCM in a SQLite database (WAL mode, 3 indices). The waking agent writes to it but **cannot read it**. The encryption key lives in `data/.dream_key` (auto-generated, `chmod 0o600`). Dream insights surface to the waking agent only through OpenClaw memory, and dream reflections optionally post to Moltbook.
133
+
134
+ ### Memory Categories
135
+
136
+ Deep memories are tagged with categories:
137
+ - `interaction` — operator conversations captured by the `agent_end` hook
138
+ - `reflection` — synthesized context from the reflection cycle
139
+ - `observation` — logged when there are no conversations or no extractable topics
140
+ - `dream_consolidation` — insights extracted from dream narratives
141
+ - `corrupted` — assigned at read time if a deep memory fails decryption
142
+
143
+ When Moltbook is enabled, additional categories may appear: `upvote`, `comment`.
144
+
145
+ ### Three Phases
146
+
147
+ ```
148
+ ┌─────────────────────────────────────────────────────────────────┐
149
+ │ DAYTIME (Reflection Cycle) │
150
+ ├─────────────────────────────────────────────────────────────────┤
151
+ │ │
152
+ │ Operator Conversations ──► Topic Extraction ──┬──► Synthesis │
153
+ │ (from hooks) (LLM) │ (LLM) │
154
+ │ │ │ │
155
+ │ Moltbook Search ◄── topics ◄─────────────────┤ │ │
156
+ │ (optional) │ ▼ │
157
+ │ │ OpenClaw │
158
+ │ Web Search ◄──── topics ◄────────────────────┘ Memory │
159
+ │ (optional) │
160
+ │ │
161
+ └─────────────────────────────────────────────────────────────────┘
162
+
163
+ ┌─────────────────────────────────────────────────────────────────┐
164
+ │ NIGHTTIME (Dream Cycle) │
165
+ ├─────────────────────────────────────────────────────────────────┤
166
+ │ │
167
+ │ Deep Memory ──► Decrypt ──► Dream Generation ──► OpenClaw │
168
+ │ (encrypted) (LLM) Memory │
169
+ │ │ │
170
+ │ ▼ │
171
+ │ Notify Operator │
172
+ │ (Telegram/Slack/etc) │
173
+ │ │ │
174
+ │ ▼ │
175
+ │ Operator Converses │
176
+ │ (feeds next cycle) │
177
+ │ │ │
178
+ │ ▼ │
179
+ │ [Optional: Moltbook] │
180
+ │ │
181
+ └─────────────────────────────────────────────────────────────────┘
182
+ ```
183
+
184
+ - **Daytime** (`src/waking.ts`): Queries deep memory for recent interactions → extracts topics via LLM → searches Moltbook (optional) and web (optional) → synthesizes context via LLM → stores reflection in deep memory
185
+ - **Night** (`src/dreamer.ts`): Decrypts all undreamed deep memories → generates surreal dream narrative via LLM → saves markdown locally → consolidates insight via LLM → stores in OpenClaw memory → notifies operator → marks memories as dreamed
186
+ - **Morning** (`src/reflection.ts`): Decomposes dream into themes → reflects in agent's voice → applies content filter → posts to Moltbook (only if enabled)
187
+
188
+ ### Module Responsibilities
189
+
190
+ | Module | Role |
191
+ |---|---|
192
+ | `src/index.ts` | OpenClaw extension entry: registers tools, hooks, scheduler service; wraps gateway into budgeted LLM client |
193
+ | `src/cli.ts` | CLI commands: `register`, `status`, `dreams` (via Commander) |
194
+ | `src/waking.ts` | Reflection cycle: conversations → topics → context → synthesis → memory |
195
+ | `src/dreamer.ts` | Dream cycle: decrypt → dream → save → consolidate → store in OpenClaw memory → notify; also `postDreamJournal` for Moltbook |
196
+ | `src/topics.ts` | Topic extraction from recent interaction deep memories |
197
+ | `src/synthesis.ts` | Context gathering orchestrator: calls topics, web-search, moltbook-search; LLM synthesis |
198
+ | `src/web-search.ts` | Web search per topic via OpenClaw `api.webSearch` |
199
+ | `src/moltbook-search.ts` | Moltbook search per topic via `MoltbookClient.search()` |
200
+ | `src/notify.ts` | Dream notification generation (LLM) and delivery via `api.channels` |
201
+ | `src/memory.ts` | Encrypted deep memory system (SQLite); `remember()`, `getRecentDeepMemories()`, `formatDeepMemoryContext()` |
202
+ | `src/crypto.ts` | `Cipher` class (AES-256-GCM); `getOrCreateDreamKey()` for key management |
203
+ | `src/reflection.ts` | Dream reflection: decompose themes, reflect in agent voice, synthesize Moltbook post |
204
+ | `src/filter.ts` | Outbound content filter: loads rules from `Moltbook-filter.md`, fail-closed on error |
205
+ | `src/persona.ts` | All system prompt templates with `{{placeholder}}` substitution via `renderTemplate()` |
206
+ | `src/moltbook.ts` | `MoltbookClient` REST client (register, post, comment, upvote, search, feed, etc.) |
207
+ | `src/budget.ts` | Daily token budget: `withBudget()` wrapper, `BudgetExceededError`, usage tracking in state |
208
+ | `src/state.ts` | `AgentState` JSON persistence with atomic writes (tmp + rename) |
209
+ | `src/config.ts` | Env loading via dotenv, path constants, memory/LLM limits; creates data directories on import |
210
+ | `src/llm.ts` | `callWithRetry()` wrapper around `p-retry`; `WAKING_RETRY_OPTS` (3 retries, 1-10s) and `DREAM_RETRY_OPTS` (3 retries, 2-20s) |
211
+ | `src/logger.ts` | Winston logger with daily-rotating file + console transport |
212
+ | `src/identity.ts` | Loads `SOUL.md` / `IDENTITY.md` from workspace dir with path traversal protection; caches result |
213
+ | `src/types.ts` | All shared TypeScript interfaces (`LLMClient`, `OpenClawAPI`, `DecryptedMemory`, `Dream`, etc.) |
214
+
215
+ ### Key Constants (from `src/config.ts`)
216
+
217
+ | Constant | Value | Usage |
218
+ |----------|-------|-------|
219
+ | `DEEP_MEMORY_CONTEXT_TOKENS` | 2000 | Token budget for deep memory context in prompts (~8000 chars) |
220
+ | `MAX_TOPICS_PER_CYCLE` | 5 | Max topics extracted per reflection cycle |
221
+ | `MAX_WEB_RESULTS_PER_TOPIC` | 3 | Web results fetched per topic |
222
+ | `MAX_MOLTBOOK_RESULTS_PER_TOPIC` | 5 | Moltbook results fetched per topic |
223
+ | `MAX_DAILY_TOKENS` | 800,000 | Daily token budget (~$20/day at Opus 4.5 output pricing) |
224
+ | `MAX_TOKENS_DREAM` | 2000 | Max output tokens for dream generation |
225
+ | `MAX_TOKENS_SYNTHESIS` | 2000 | Max output tokens for context synthesis |
226
+ | `MAX_TOKENS_REFLECTION` | 1500 | Max output tokens for dream reflection |
227
+ | `MAX_TOKENS_TOPIC_EXTRACTION` | 500 | Max output tokens for topic extraction |
228
+ | `MAX_TOKENS_SUMMARY` | 150 | Max output tokens for summaries |
229
+ | `MAX_TOKENS_CONSOLIDATION` | 150 | Max output tokens for dream consolidation |
230
+
231
+ ### Data Directory Layout
232
+
233
+ All runtime data lives under `data/` (auto-created by `config.ts`, gitignored):
234
+
235
+ ```
236
+ data/
237
+ ├── memory/
238
+ │ ├── deep.db # Encrypted deep memory (SQLite, WAL mode)
239
+ │ ├── deep.db-wal # SQLite WAL file
240
+ │ ├── deep.db-shm # SQLite shared memory
241
+ │ └── state.json # Agent state (last_check, total_dreams, budget, etc.)
242
+ ├── dreams/
243
+ │ └── YYYY-MM-DD_slug.md # Dream narrative markdown files
244
+ ├── .dream_key # AES-256 key (base64, chmod 600) — security-critical
245
+ ├── credentials.json # Moltbook API credentials (if registered)
246
+ └── electricsheep-YYYY-MM-DD.log # Daily rotating log files
247
+ ```
248
+
249
+ The encryption key at `data/.dream_key` enforces the separation between waking and dreaming states. It is created with exclusive mode (`wx` flag) and `0o600` permissions.
250
+
251
+ ## Cost & API Usage
252
+
253
+ Each reflection cycle makes 2-3 LLM calls (topic extraction, synthesis, summary). Each dream cycle makes 2-3 (dream generation, consolidation, optional notification message). The default schedule (4 reflections + 1 dream per day) produces ~10-15 API calls/day.
254
+
255
+ ### Daily Token Budget
256
+
257
+ `src/budget.ts` implements a best-effort daily kill switch. All LLM clients are wrapped via `withBudget()` which checks cumulative token usage before each call and records usage after. Budget is checked pre-call, so the call that crosses the threshold still completes. Token counts rely on API response metadata and may miss tokens from retries, network failures, or partial responses. Usage is tracked in `state.json` (`budget_date`, `budget_tokens_used`) and resets at midnight UTC. Default limit: 800K tokens. Set `MAX_DAILY_TOKENS=0` to disable. The `LLMClient` interface returns `{ text, usage? }` so the OpenClaw gateway reports token counts.
258
+
259
+ ## Test Coverage
260
+
261
+ Tests live in `test/` and use `node:test` with `tsx`. Each test creates an isolated temp dir via `ELECTRICSHEEP_DATA_DIR`.
262
+
263
+ | Test File | Modules Covered |
264
+ |---|---|
265
+ | `test/crypto.test.ts` | `Cipher`, `getOrCreateDreamKey` |
266
+ | `test/memory.test.ts` | Deep memory functions, `getRecentDeepMemories`, `formatDeepMemoryContext`, `remember` |
267
+ | `test/state.test.ts` | `loadState`, `saveState` |
268
+ | `test/budget.test.ts` | `withBudget`, budget tracking, `BudgetExceededError` |
269
+ | `test/persona.test.ts` | `renderTemplate`, prompt template validation |
270
+ | `test/dreamer.test.ts` | `runDreamCycle` with mock LLM |
271
+ | `test/waking.test.ts` | `runReflectionCycle` with mock LLM |
272
+ | `test/reflection.test.ts` | `reflectOnDreamJournal` |
273
+ | `test/filter.test.ts` | `applyFilter`, `clearFilterCache` |
274
+ | `test/moltbook.test.ts` | `MoltbookClient` (with `globalThis.fetch` mocking) |
275
+
276
+ Not currently tested: `index.ts`, `topics.ts`, `synthesis.ts`, `web-search.ts`, `moltbook-search.ts`, `notify.ts`, `identity.ts`, `llm.ts`, `cli.ts`, `config.ts`, `logger.ts`.
277
+
278
+ ## Dependencies
279
+
280
+ **Runtime:** `better-sqlite3`, `commander`, `chalk`, `winston`, `winston-daily-rotate-file`, `p-retry`, `dotenv`
281
+
282
+ **Peer:** `openclaw` (>=1.0.0)
283
+
284
+ **Dev:** `typescript`, `tsx`, `eslint`, `typescript-eslint`, `eslint-config-prettier`, `prettier`, `standard-version`, `@types/better-sqlite3`, `@types/node`
285
+
286
+ **Node:** >=24.0.0 | **Module system:** ESM (`"type": "module"`) | **Target:** ES2023