返回 JoyAI-Echo
memory.md
1 # Memory in nanobot
2
3 nanobot's memory is built on a simple belief: memory should feel alive, but it should not feel chaotic.
4
5 Good memory is not a pile of notes. It is a quiet system of attention. It notices what is worth keeping, lets go of what no longer needs the spotlight, and turns lived experience into something calm, durable, and useful.
6
7 That is the shape of memory in nanobot.
8
9 ## The Design
10
11 nanobot does not treat memory as one giant file.
12
13 It separates memory into layers, because different kinds of remembering deserve different tools:
14
15 - `session.messages` holds the living short-term conversation.
16 - `memory/history.jsonl` is the running archive of compressed past turns.
17 - `SOUL.md`, `USER.md`, and `memory/MEMORY.md` are the durable knowledge files.
18 - `GitStore` records how those durable files change over time.
19
20 This keeps the system light in the moment, but reflective over time.
21
22 ## The Flow
23
24 Memory moves through nanobot in two stages.
25
26 ### Stage 1: Consolidator
27
28 When a conversation grows large enough to pressure the context window, nanobot does not try to carry every old message forever.
29
30 Instead, the `Consolidator` summarizes the oldest safe slice of the conversation and appends that summary to `memory/history.jsonl`.
31
32 This file is:
33
34 - append-only
35 - cursor-based
36 - optimized for machine consumption first, human inspection second
37
38 Each line is a JSON object:
39
40 ```json
41 {"cursor": 42, "timestamp": "2026-04-03 00:02", "content": "- User prefers dark mode\n- Decided to use PostgreSQL"}
42 ```
43
44 It is not the final memory. It is the material from which final memory is shaped.
45
46 ### Stage 2: Dream
47
48 `Dream` is the slower, more thoughtful layer. It runs on a cron schedule by default and can also be triggered manually.
49
50 Dream reads:
51
52 - new entries from `memory/history.jsonl`
53 - the current `SOUL.md`
54 - the current `USER.md`
55 - the current `memory/MEMORY.md`
56
57 Then it works in two phases:
58
59 1. It studies what is new and what is already known.
60 2. It edits the long-term files surgically, not by rewriting everything, but by making the smallest honest change that keeps memory coherent.
61
62 This is why nanobot's memory is not just archival. It is interpretive.
63
64 ## The Files
65
66 ```text
67 workspace/
68 ├── SOUL.md # The bot's long-term voice and communication style
69 ├── USER.md # Stable knowledge about the user
70 └── memory/
71 ├── MEMORY.md # Project facts, decisions, and durable context
72 ├── history.jsonl # Append-only history summaries
73 ├── .cursor # Consolidator write cursor
74 ├── .dream_cursor # Dream consumption cursor
75 └── .git/ # Version history for long-term memory files
76 ```
77
78 These files play different roles:
79
80 - `SOUL.md` remembers how nanobot should sound.
81 - `USER.md` remembers who the user is and what they prefer.
82 - `MEMORY.md` remembers what remains true about the work itself.
83 - `history.jsonl` remembers what happened on the way there.
84
85 ## Why `history.jsonl`
86
87 The old `HISTORY.md` format was pleasant for casual reading, but it was too fragile as an operational substrate.
88
89 `history.jsonl` gives nanobot:
90
91 - stable incremental cursors
92 - safer machine parsing
93 - easier batching
94 - cleaner migration and compaction
95 - a better boundary between raw history and curated knowledge
96
97 You can still search it with familiar tools:
98
99 ```bash
100 # grep
101 grep -i "keyword" memory/history.jsonl
102
103 # jq
104 cat memory/history.jsonl | jq -r 'select(.content | test("keyword"; "i")) | .content' | tail -20
105
106 # Python
107 python -c "import json; [print(json.loads(l).get('content','')) for l in open('memory/history.jsonl','r',encoding='utf-8') if l.strip() and 'keyword' in l.lower()][-20:]"
108 ```
109
110 The difference is philosophical as much as technical:
111
112 - `history.jsonl` is for structure
113 - `SOUL.md`, `USER.md`, and `MEMORY.md` are for meaning
114
115 ## Commands
116
117 Memory is not hidden behind the curtain. Users can inspect and guide it.
118
119 | Command | What it does |
120 |---------|--------------|
121 | `/dream` | Run Dream immediately |
122 | `/dream-log` | Show the latest Dream memory change |
123 | `/dream-log <sha>` | Show a specific Dream change |
124 | `/dream-restore` | List recent Dream memory versions |
125 | `/dream-restore <sha>` | Restore memory to the state before a specific change |
126
127 These commands exist for a reason: automatic memory is powerful, but users should always retain the right to inspect, understand, and restore it.
128
129 ## Versioned Memory
130
131 After Dream changes long-term memory files, nanobot can record that change with `GitStore`.
132
133 This gives memory a history of its own:
134
135 - you can inspect what changed
136 - you can compare versions
137 - you can restore a previous state
138
139 That turns memory from a silent mutation into an auditable process.
140
141 ## Configuration
142
143 Dream is configured under `agents.defaults.dream`:
144
145 ```json
146 {
147 "agents": {
148 "defaults": {
149 "dream": {
150 "intervalH": 2,
151 "modelOverride": null,
152 "maxBatchSize": 20,
153 "maxIterations": 10
154 }
155 }
156 }
157 }
158 ```
159
160 | Field | Meaning |
161 |-------|---------|
162 | `intervalH` | How often Dream runs, in hours |
163 | `modelOverride` | Optional Dream-specific model override |
164 | `maxBatchSize` | How many history entries Dream processes per run |
165 | `maxIterations` | The tool budget for Dream's editing phase |
166
167 In practical terms:
168
169 - `modelOverride: null` means Dream uses the same model as the main agent. Set it only if you want Dream to run on a different model.
170 - `maxBatchSize` controls how many new `history.jsonl` entries Dream consumes in one run. Larger batches catch up faster; smaller batches are lighter and steadier.
171 - `maxIterations` limits how many read/edit steps Dream can take while updating `SOUL.md`, `USER.md`, and `MEMORY.md`. It is a safety budget, not a quality score.
172 - `intervalH` is the normal way to configure Dream. Internally it runs as an `every` schedule, not as a cron expression.
173
174 Legacy note:
175
176 - Older source-based configs may still contain `dream.cron`. nanobot continues to honor it for backward compatibility, but new configs should use `intervalH`.
177 - Older source-based configs may still contain `dream.model`. nanobot continues to honor it for backward compatibility, but new configs should use `modelOverride`.
178
179 ## In Practice
180
181 What this means in daily use is simple:
182
183 - conversations can stay fast without carrying infinite context
184 - durable facts can become clearer over time instead of noisier
185 - the user can inspect and restore memory when needed
186
187 Memory should not feel like a dump. It should feel like continuity.
188
189 That is what this design is trying to protect.
190
190 lines MARKDOWN