@basicmemory/openclaw-basic-memory 0.1.0-alpha.9 → 0.1.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/README.md CHANGED
@@ -1,134 +1,66 @@
1
1
  # openclaw-basic-memory
2
2
 
3
- Local-first knowledge graph plugin for OpenClaw — persistent memory with graph search and composited memory search. Everything works locally with no cloud account required.
3
+ Give your OpenClaw agent persistent, searchable memory — in plain text files you can read and edit.
4
4
 
5
- ## What this plugin does
5
+ ## What is Basic Memory?
6
6
 
7
- The `openclaw-basic-memory` plugin integrates [Basic Memory](https://github.com/basicmachines-co/basic-memory) with OpenClaw to provide:
7
+ [Basic Memory](https://basicmemory.com) stores AI knowledge in local Markdown files and indexes them into a semantic knowledge graph. Your agent writes notes. You can open them in any editor, read them, change them, and the changes sync back automatically. No black box. No proprietary format. Just files.
8
8
 
9
- - **Composited `memory_search`** — queries MEMORY.md, the BM knowledge graph, and active tasks in parallel
10
- - **Persistent MCP stdio session** — keeps a single `bm mcp --transport stdio --project <name>` process alive
11
- - **Auto-recall** — injects active tasks and recent activity as context at session start
12
- - **Auto-capture** — records agent conversations as structured daily notes
13
- - **Graph tools** — search, read, write, edit, delete, move, and navigate notes via `memory://` URLs
14
- - **Skill workflows** — `/tasks`, `/reflect`, `/defrag`, `/schema` slash commands for guided memory management
9
+ It does three things that work together:
15
10
 
16
- All data stays on your machine as markdown files indexed locally with SQLite. Cloud sync is available but entirely optional — see [BASIC_MEMORY.md](./BASIC_MEMORY.md) for cloud setup.
11
+ - **Stores knowledge in plain Markdown** — everything lives in plain text files on your computer, not locked inside a database you can't read
12
+ - **Creates connections automatically** — notes link to each other through a searchable, traversable knowledge graph
13
+ - **Searches by meaning, not just keywords** — vector search finds relevant context even when the exact words don't match
14
+ - **Keeps notes consistent** — dynamic schemas and validation ensure your knowledge base stays structured and useful as it grows
15
+ - **Enables two-way collaboration** — both you and the AI read and write the same files
17
16
 
18
- For a practical runbook, see [Memory + Task Flow](./MEMORY_TASK_FLOW.md).
17
+ Over time, your agent builds a knowledge base that grows with you. Context that survives across sessions. Memory that belongs to you.
19
18
 
20
- ## Requirements
19
+ Learn more: [basicmemory.com](https://basicmemory.com) · [GitHub](https://github.com/basicmachines-co/basic-memory) · [Docs](https://docs.basicmemory.com)
21
20
 
22
- 1. **[uv](https://docs.astral.sh/uv/)** — Python package manager used to install Basic Memory CLI
23
- ```bash
24
- # macOS
25
- brew install uv
21
+ ## What this plugin does
26
22
 
27
- # macOS / Linux
28
- curl -LsSf https://astral.sh/uv/install.sh | sh
29
- ```
23
+ This plugin connects Basic Memory to OpenClaw so your agent can:
30
24
 
31
- 2. **[OpenClaw](https://docs.openclaw.ai)** with plugin support
25
+ - **Remember across sessions** — search and recall past conversations, decisions, and context
26
+ - **Track work in progress** — structured tasks that survive context compaction
27
+ - **Build knowledge over time** — notes, observations, and relations that grow into a connected graph
28
+ - **Search intelligently** — composited search across working memory, the knowledge graph, and active tasks in parallel
32
29
 
33
- ## Installation
30
+ All data stays on your machine as Markdown files indexed locally with SQLite. Cloud sync is available but entirely optional.
34
31
 
35
- ```bash
36
- # Install the plugin (automatically installs the bm CLI via uv)
37
- openclaw plugins install @basicmemory/openclaw-basic-memory
32
+ ## Install
38
33
 
39
- # Enable and assign to the memory slot
40
- openclaw plugins enable openclaw-basic-memory --slot memory
34
+ **Prerequisite:** [uv](https://docs.astral.sh/uv/) (Python package manager) — used to install the Basic Memory CLI.
41
35
 
42
- # Restart the gateway
43
- openclaw gateway restart
44
- ```
45
-
46
- Verify:
47
36
  ```bash
48
- openclaw plugins list
49
- openclaw plugins info openclaw-basic-memory
50
- ```
37
+ # macOS
38
+ brew install uv
51
39
 
52
- If `uv` is not installed, the `bm` CLI setup is skipped gracefully during install. Install `uv` first, then re-run the postinstall script:
53
-
54
- ```bash
55
- bash ~/.openclaw/extensions/openclaw-basic-memory/scripts/setup-bm.sh
40
+ # macOS / Linux
41
+ curl -LsSf https://astral.sh/uv/install.sh | sh
56
42
  ```
57
43
 
58
- ### Basic Memory Cloud
59
-
60
- Everything works locally — cloud adds cross-device, team, and production capabilities:
61
-
62
- - **Your agent's memory travels with you** — same knowledge graph on laptop, desktop, and hosted environments
63
- - **Team knowledge sharing** — org workspaces let multiple agents and team members build on a shared knowledge base
64
- - **Durable memory for production agents** — persistent memory that survives CI teardowns and container restarts
65
- - **Multi-agent coordination** — multiple agents can read and write to the same graph
66
-
67
- Cloud extends local-first — still plain markdown, still yours. Start with a [7-day free trial](https://basicmemory.com) and use code `BMCLAW` for 20% off for 3 months. See [BASIC_MEMORY.md](./BASIC_MEMORY.md) for cloud setup.
68
-
69
- ### Development (local directory)
70
-
71
- For plugin development, clone and link locally:
44
+ Then install the plugin:
72
45
 
73
46
  ```bash
74
- git clone https://github.com/basicmachines-co/openclaw-basic-memory.git
75
- cd openclaw-basic-memory
76
- bun install
77
- openclaw plugins install -l "$PWD"
78
- openclaw plugins enable openclaw-basic-memory --slot memory
47
+ openclaw plugins install @basicmemory/openclaw-basic-memory
79
48
  openclaw gateway restart
80
49
  ```
81
50
 
82
- Or load directly from a path in your OpenClaw config:
51
+ That's it. The plugin auto-installs the `bm` CLI on first startup if it's not already on your PATH. See [SECURITY.md](./SECURITY.md) for details on how this works.
83
52
 
84
- ```json5
85
- {
86
- plugins: {
87
- load: {
88
- paths: ["~/dev/openclaw-basic-memory"]
89
- },
90
- entries: {
91
- "openclaw-basic-memory": {
92
- enabled: true
93
- }
94
- },
95
- slots: {
96
- memory: "openclaw-basic-memory"
97
- }
98
- }
99
- }
100
- ```
101
-
102
- ### Bundled Skills
103
-
104
- This plugin ships with six skills that are automatically available when the plugin is enabled — no manual installation needed:
105
-
106
- - **`memory-tasks`** — structured task tracking that survives context compaction
107
- - **`memory-reflect`** — periodic consolidation of recent notes into durable memory
108
- - **`memory-defrag`** — periodic cleanup/reorganization of memory files
109
- - **`memory-schema`** — schema lifecycle management (infer, create, validate, diff, evolve)
110
- - **`memory-metadata-search`** — structured metadata search by custom frontmatter fields (status, priority, etc.)
111
- - **`memory-notes`** — guidance for writing well-structured notes with observations and relations
112
-
113
- Skills are loaded from the plugin's `skills/` directory. See the upstream source at [`basic-memory-skills`](https://github.com/basicmachines-co/basic-memory-skills).
114
-
115
- #### Updating or adding skills with `npx skills`
116
-
117
- You can update bundled skills or install new ones as they become available using the [Skills CLI](https://github.com/vercel-labs/skills):
53
+ Verify:
118
54
 
119
55
  ```bash
120
- # Update all basic-memory skills to latest
121
- npx skills add basicmachines-co/basic-memory-skills --agent openclaw
122
-
123
- # Install a specific skill
124
- npx skills add basicmachines-co/basic-memory-skills --name memory-tasks --agent openclaw
56
+ openclaw plugins list
57
+ openclaw plugins info openclaw-basic-memory
125
58
  ```
126
59
 
127
- This installs to the same `skills/` directory the plugin reads from, so updated skills take effect on the next session.
128
-
129
60
  ## Configuration
130
61
 
131
- ### Minimal (zero-config)
62
+ ### Zero-config (recommended)
63
+
132
64
  ```json5
133
65
  {
134
66
  "openclaw-basic-memory": {
@@ -137,476 +69,180 @@ This installs to the same `skills/` directory the plugin reads from, so updated
137
69
  }
138
70
  ```
139
71
 
140
- This uses sensible defaults: auto-generated project name, maps Basic Memory to your workspace root, sets it as the default BM project, and captures conversations.
72
+ This uses sensible defaults: auto-generated project name, maps to your workspace root, captures conversations, and recalls active tasks on session start.
73
+
74
+ ### Full options
141
75
 
142
- ### Full configuration
143
76
  ```json5
144
77
  {
145
78
  "openclaw-basic-memory": {
146
79
  enabled: true,
147
80
  config: {
148
- project: "my-agent", // BM project name (default: "openclaw-{hostname}")
149
- bmPath: "bm", // Path to BM CLI binary
150
- projectPath: ".", // Defaults to workspace root; supports absolute, ~/..., or workspace-relative paths
151
- memoryDir: "memory/", // Relative memory dir for task scanning
152
- memoryFile: "MEMORY.md", // Working memory file for grep search
153
- autoCapture: true, // Index conversations automatically
154
- captureMinChars: 10, // Min chars to trigger auto-capture
155
- autoRecall: true, // Inject active tasks + recent activity at session start
156
- recallPrompt: "Check for active tasks and recent activity. Summarize anything relevant to the current session.",
157
- debug: false, // Verbose logging
81
+ project: "my-agent", // BM project name (default: "openclaw-{hostname}")
82
+ projectPath: ".", // Project directory (default: workspace root)
83
+ memoryDir: "memory/", // Where task notes live
84
+ memoryFile: "MEMORY.md", // Working memory file
85
+ autoCapture: true, // Record conversations as daily notes
86
+ autoRecall: true, // Inject active tasks + recent activity at session start
87
+ debug: false // Verbose logging
158
88
  }
159
89
  }
160
90
  }
161
91
  ```
162
92
 
163
- ### Configuration Options
164
-
165
- | Option | Type | Default | Description |
166
- |--------|------|---------|-------------|
167
- | `project` | string | `"openclaw-{hostname}"` | Basic Memory project name |
168
- | `bmPath` | string | `"bm"` | Path to Basic Memory CLI binary |
169
- | `projectPath` | string | `"."` | Directory for BM project data (defaults to workspace root; resolved from workspace unless absolute) |
170
- | `memoryDir` | string | `"memory/"` | Relative path for task scanning |
171
- | `memoryFile` | string | `"MEMORY.md"` | Working memory file (grep-searched) |
172
- | `autoCapture` | boolean | `true` | Auto-index agent conversations |
173
- | `captureMinChars` | number | `10` | Minimum character threshold for auto-capture (both messages must be shorter to skip) |
174
- | `autoRecall` | boolean | `true` | Inject active tasks and recent activity as context at session start |
175
- | `recallPrompt` | string | *(see above)* | Instruction appended to recalled context — customize to change what the agent focuses on |
176
- | `debug` | boolean | `false` | Enable verbose debug logs |
177
-
178
- Snake_case aliases (`memory_dir`, `memory_file`, `auto_recall`, `recall_prompt`, `capture_min_chars`) are also supported.
179
-
180
- Cloud sync is optional — see [BASIC_MEMORY.md](./BASIC_MEMORY.md) for cloud configuration.
181
-
182
- On startup, the plugin ensures the configured BM project exists at `projectPath` via MCP `create_memory_project` in idempotent mode, and sets it as the default Basic Memory project.
183
-
184
- ## How It Works
185
-
186
- ### MCP Session Lifecycle
187
- On startup, the plugin starts one persistent MCP stdio session:
188
- 1. Spawns `bm mcp --transport stdio --project <name>`
189
- 2. Verifies required MCP tool capabilities at connect time
190
- 3. Uses bounded reconnect attempts (`500ms`, `1000ms`, `2000ms`) when the session drops
191
-
192
- Basic Memory MCP lifecycle handles sync and watch behavior for the project.
193
-
194
- ### Composited `memory_search`
195
- When the agent calls `memory_search`, three sources are queried in parallel:
196
-
197
- 1. **MEMORY.md** — grep/text search with ±1 line context
198
- 2. **BM Knowledge Graph** — hybrid FTS + vector search (top 5 results with scores)
199
- 3. **Active Tasks** — scans `memory/tasks/` for non-done tasks
200
-
201
- Results are formatted into clear sections:
202
- ```
203
- ## MEMORY.md
204
- - matching lines with context...
205
-
206
- ## Knowledge Graph (memory/)
207
- - note-title (0.85)
208
- > preview of content...
209
-
210
- ## Active Tasks
211
- - **Task Name** (status: active, step: 3)
212
- context snippet...
213
- ```
214
-
215
- ### Memory + Task Management Flow
216
-
217
- This plugin works best if you treat memory as three lanes:
218
-
219
- 1. **Working memory (`MEMORY.md`)** — short-horizon context and current focus.
220
- 2. **Knowledge graph (`memory/**/*.md`)** — long-term notes indexed by Basic Memory.
221
- 3. **Task notes (`memory/tasks/*.md`)** — active execution state for in-flight work.
222
-
223
- > **Note:** OpenClaw's default convention treats `MEMORY.md` as [long-term curated memory](https://docs.openclaw.ai/concepts/memory). This plugin flips that role — the BM knowledge graph handles durable storage, so `MEMORY.md` serves as short-horizon working memory. See [MEMORY_TASK_FLOW.md](./MEMORY_TASK_FLOW.md) for details.
224
-
225
- Typical loop:
226
-
227
- 1. Capture or update notes/tasks with `write_note` / `edit_note`.
228
- 2. The persistent BM MCP process syncs markdown updates into the BM project index.
229
- 3. `memory_search` queries:
230
- - `MEMORY.md` text snippets
231
- - BM search results (semantic + FTS)
232
- - active tasks
233
- 4. Drill into one result with `memory_get` or `read_note`.
234
- 5. Advance tasks by updating `current_step`, checkboxes, and context.
235
- 6. Complete tasks by setting `status: done` (done tasks are excluded from active task results).
236
-
237
- ```mermaid
238
- flowchart LR
239
- A["Write/Update notes"] --> B["BM MCP indexes changes"]
240
- B --> C["memory_search(query)"]
241
- C --> D["MEMORY.md"]
242
- C --> E["Knowledge Graph"]
243
- C --> F["Active Tasks"]
244
- D --> G["Composited result"]
245
- E --> G
246
- F --> G
247
- G --> H["memory_get / read_note"]
248
- H --> A
249
- ```
250
-
251
- ### Task Note Shape (Recommended)
252
-
253
- `memory_search` task extraction is strongest when task notes include:
254
-
255
- - file location: `memory/tasks/*.md`
256
- - frontmatter fields: `status:` and `current_step:`
257
- - a `## Context` section for preview snippets
258
-
259
- Example:
260
-
261
- ```markdown
262
- ---
263
- title: auth-middleware-rollout
264
- type: Task
265
- status: active
266
- current_step: 2
267
- ---
268
-
269
- ## Context
270
- Rolling JWT middleware to all API routes. Staging verification is in progress.
271
-
272
- ## Plan
273
- - [x] Implement middleware
274
- - [x] Add refresh-token validation
275
- - [ ] Roll out to staging
276
- - [ ] Verify logs and error rates
277
- ```
278
-
279
- To mark complete, update:
280
-
281
- ```yaml
282
- status: done
283
- ```
284
-
285
- Done tasks are filtered out of the `Active Tasks` section in composited `memory_search`.
286
-
287
- ### Auto-Recall
288
- On each `agent_start` event (when `autoRecall: true`), the plugin:
289
- 1. Queries the knowledge graph for active tasks (`type: Task`, `status: active`, up to 5)
290
- 2. Fetches notes modified in the last 24 hours
291
- 3. Formats both into structured context and returns it to the agent
292
-
293
- This gives the agent immediate awareness of ongoing work and recent changes without the user needing to ask. The `recallPrompt` config field controls the instruction appended to the context — customize it to steer what the agent prioritizes.
93
+ | Option | Default | Description |
94
+ |--------|---------|-------------|
95
+ | `project` | `"openclaw-{hostname}"` | Basic Memory project name |
96
+ | `bmPath` | `"bm"` | Path to BM CLI binary |
97
+ | `projectPath` | `"."` | Project data directory |
98
+ | `memoryDir` | `"memory/"` | Relative path for task scanning |
99
+ | `memoryFile` | `"MEMORY.md"` | Working memory file for text search |
100
+ | `autoCapture` | `true` | Auto-index agent conversations |
101
+ | `captureMinChars` | `10` | Min chars to trigger capture |
102
+ | `autoRecall` | `true` | Inject context at session start |
103
+ | `recallPrompt` | *(default)* | Instruction appended to recalled context |
104
+ | `debug` | `false` | Verbose logs |
294
105
 
295
- ### Auto-Capture
296
- After each agent turn (when `autoCapture: true`), the plugin:
297
- 1. Extracts the last user + assistant messages
298
- 2. Appends them as timestamped entries to a daily conversation note (`conversations-YYYY-MM-DD`)
299
- 3. Skips very short exchanges (< `captureMinChars` chars each, default 10)
106
+ ## How it works
300
107
 
301
- ## Agent Tools
108
+ ### Memory search
302
109
 
303
- All content tools accept an optional `project` parameter to operate on a different project than the default (cross-project operations).
110
+ When your agent calls `memory_search`, three sources are queried in parallel:
304
111
 
305
- ### `list_workspaces`
306
- List all workspaces (personal and organization) accessible to this user.
307
- ```
308
- list_workspaces()
309
- ```
112
+ 1. **MEMORY.md** — text search with surrounding context
113
+ 2. **Knowledge Graph** — hybrid full-text + vector search across all indexed notes
114
+ 3. **Active Tasks** — scans `memory/tasks/` for in-progress work
310
115
 
311
- ### `list_memory_projects`
312
- List all projects, optionally filtered by workspace.
313
- ```
314
- list_memory_projects()
315
- list_memory_projects(workspace="my-org")
316
- ```
116
+ Results come back in clear sections so the agent knows where each piece of context came from.
317
117
 
318
- ### `search_notes`
319
- Search the knowledge graph.
320
- ```
321
- search_notes(query="API design", limit=5)
322
- search_notes(query="API design", project="other-project") # cross-project
323
- ```
118
+ ### Auto-recall
324
119
 
325
- ### `read_note`
326
- Read a note by title, permalink, or `memory://` URL.
327
- ```
328
- read_note(identifier="memory://projects/api-redesign")
329
- read_note(identifier="memory://projects/api-redesign", include_frontmatter=true) # raw markdown + YAML
330
- read_note(identifier="notes/readme", project="docs") # cross-project
331
- ```
120
+ On each session start, the plugin loads active tasks and recently modified notes, giving the agent immediate awareness of ongoing work without being asked.
332
121
 
333
- ### `write_note`
334
- Create a new note.
335
- ```
336
- write_note(title="Auth Strategy", content="## Overview\n...", folder="decisions")
337
- write_note(title="Shared Note", content="...", folder="shared", project="team") # cross-project
338
- ```
122
+ ### Auto-capture
339
123
 
340
- ### `edit_note`
341
- Edit an existing note (`append`, `prepend`, `find_replace`, `replace_section`).
342
- ```
343
- edit_note(identifier="weekly-review", operation="append", content="\n## Update\nDone.")
344
- edit_note(
345
- identifier="weekly-review",
346
- operation="find_replace",
347
- find_text="status: active",
348
- content="status: done",
349
- expected_replacements=1,
350
- )
351
- edit_note(
352
- identifier="weekly-review",
353
- operation="replace_section",
354
- section="## This Week",
355
- content="- Done\n- Next",
356
- )
357
- ```
124
+ After each conversation turn, the plugin records the exchange as a timestamped entry in a daily note. This builds a searchable history of everything your agent has discussed.
358
125
 
359
- ### `delete_note`
360
- Delete a note.
361
- ```
362
- delete_note(identifier="notes/old-draft")
363
- delete_note(identifier="notes/old-draft", project="archive") # cross-project
364
- ```
126
+ ### Persistent connection
365
127
 
366
- ### `move_note`
367
- Move a note to a different folder.
368
- ```
369
- move_note(identifier="notes/my-note", newFolder="archive")
370
- ```
128
+ The plugin keeps a long-lived Basic Memory process running over standard I/O. No cold starts per tool call. The connection auto-reconnects if it drops.
371
129
 
372
- ### `build_context`
373
- Navigate the knowledge graph — get a note with its observations and relations.
374
- ```
375
- build_context(url="memory://projects/api-redesign", depth=2)
376
- build_context(url="memory://decisions", depth=1, project="team") # cross-project
377
- ```
130
+ ## Agent tools
378
131
 
379
- ### `schema_validate`
380
- Validate notes against their Picoschema definitions.
381
- ```
382
- schema_validate(noteType="person")
383
- schema_validate(identifier="notes/john-doe")
384
- schema_validate(noteType="person", project="contacts") # cross-project
385
- ```
132
+ All tools accept an optional `project` parameter for cross-project operations.
386
133
 
387
- ### `schema_infer`
388
- Analyze existing notes and suggest a Picoschema definition.
389
- ```
390
- schema_infer(noteType="meeting")
391
- schema_infer(noteType="person", threshold=0.5)
392
- ```
393
-
394
- ### `schema_diff`
395
- Detect drift between a schema definition and actual note usage.
396
- ```
397
- schema_diff(noteType="person")
398
- ```
399
-
400
- ## Slash Commands
134
+ | Tool | Description |
135
+ |------|-------------|
136
+ | `memory_search` | Composited search across all memory sources |
137
+ | `memory_get` | Read a specific note by title or path |
138
+ | `search_notes` | Search the knowledge graph directly |
139
+ | `read_note` | Read a note by title, permalink, or `memory://` URL |
140
+ | `write_note` | Create or update a note |
141
+ | `edit_note` | Append, prepend, find/replace, or replace a section |
142
+ | `delete_note` | Delete a note |
143
+ | `move_note` | Move a note to a different folder |
144
+ | `build_context` | Navigate the knowledge graph — follow relations and connections |
145
+ | `list_memory_projects` | List accessible projects |
146
+ | `list_workspaces` | List workspaces (personal and org) |
147
+ | `schema_validate` | Validate notes against Picoschema definitions |
148
+ | `schema_infer` | Analyze notes and suggest a schema |
149
+ | `schema_diff` | Detect drift between schema and actual usage |
401
150
 
402
- ### Memory
403
- - **`/remember <text>`** — Save a quick note to the knowledge graph
404
- - **`/recall <query>`** — Search the knowledge graph (top 5 results)
405
-
406
- ### Skill workflows
407
- These inject step-by-step instructions from the bundled skill files. Each accepts optional arguments for context.
151
+ ## Slash commands
408
152
 
409
153
  | Command | Description |
410
154
  |---------|-------------|
411
- | `/tasks [args]` | Task management — create, track, resume structured tasks |
412
- | `/reflect [args]` | Memory reflection — consolidate recent activity into long-term memory |
413
- | `/defrag [args]` | Memory defrag — reorganize, split, prune memory files |
414
- | `/schema [args]` | Schema management — infer, create, validate, evolve Picoschema definitions |
155
+ | `/remember <text>` | Save a quick note |
156
+ | `/recall <query>` | Search the knowledge graph |
157
+ | `/tasks [args]` | Create, track, resume structured tasks |
158
+ | `/reflect [args]` | Consolidate recent notes into long-term memory |
159
+ | `/defrag [args]` | Reorganize and clean up memory files |
160
+ | `/schema [args]` | Manage Picoschema definitions |
415
161
 
416
- Examples:
417
- ```
418
- /tasks create a task for the API migration
419
- /reflect
420
- /defrag clean up completed tasks older than 2 weeks
421
- /schema infer a schema for Meeting notes
422
- ```
423
-
424
- ## CLI Commands
162
+ ## CLI
425
163
 
426
164
  ```bash
427
165
  openclaw basic-memory search "auth patterns" --limit 5
428
166
  openclaw basic-memory read "projects/api-redesign"
429
- openclaw basic-memory read "projects/api-redesign" --raw
430
- openclaw basic-memory edit "projects/api-redesign" --operation append --content $'\n## Update\nDone.'
431
167
  openclaw basic-memory context "memory://projects/api-redesign" --depth 2
432
168
  openclaw basic-memory recent --timeframe 24h
433
169
  openclaw basic-memory status
434
170
  ```
435
171
 
436
- ## Troubleshooting
437
-
438
- ### `bm` command not found
439
- ```bash
440
- which bm # Check if installed
441
- bm --version # Check version
442
- bm mcp --help # Verify MCP server command exists
443
- ```
444
- If `bm mcp` doesn't exist, update Basic Memory to a newer version.
445
-
446
- ### `edit_note` says `edit-note` is required
447
- Your installed `basic-memory` version is missing native `tool edit-note`.
448
- Upgrade `basic-memory` and rerun.
449
-
450
- ### Jiti cache issues
451
- ```bash
452
- rm -rf /tmp/jiti/ "$TMPDIR/jiti/"
453
- openclaw gateway stop && openclaw gateway start
454
- ```
455
-
456
- ### Disabling semantic search
457
- If you want to run without vector/embedding dependencies (faster startup, less memory), set the environment variable before launching:
458
- ```bash
459
- BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=false
460
- ```
461
- Or in `~/.basic-memory/config.json`:
462
- ```json
463
- { "semantic_search_enabled": false }
464
- ```
465
- Search will fall back to full-text search only.
466
-
467
- ### Search returns no results
468
- 1. Check that the MCP session is connected (look for `connected to BM MCP stdio` in logs)
469
- 2. Verify files exist in the project directory
470
- 3. Try `bm mcp --transport stdio --project <name>` and run `search_notes` through an MCP inspector/client
471
- 4. Check project status: `bm project list`
472
-
473
- ## Integration Tests
172
+ ## Bundled skills
474
173
 
475
- This repo includes real end-to-end integration tests for `BmClient` in:
174
+ Six skills ship with the plugin — no installation needed:
476
175
 
477
- - `integration/bm-client.integration.test.ts`
176
+ - **memory-tasks** — structured task tracking that survives context compaction
177
+ - **memory-reflect** — periodic consolidation of recent notes into durable memory
178
+ - **memory-defrag** — cleanup and reorganization of memory files
179
+ - **memory-schema** — schema lifecycle (infer, create, validate, diff)
180
+ - **memory-metadata-search** — query notes by frontmatter fields
181
+ - **memory-notes** — guidance for writing well-structured notes
478
182
 
479
- These tests launch a real `bm mcp --transport stdio --project <name>` process,
480
- run write/read/edit/search/context/move/delete calls, and assert actual filesystem/index results.
481
-
482
- Run integration tests:
183
+ ### Updating skills
483
184
 
484
185
  ```bash
485
- bun run test:int
186
+ npx skills add basicmachines-co/basic-memory-skills --agent openclaw
486
187
  ```
487
188
 
488
- By default this uses `./scripts/bm-local.sh`, which runs BM from a sibling
489
- `../basic-memory` checkout via `uv run --project ...` when present, and falls
490
- back to `bm` on `PATH` otherwise.
189
+ ## Task notes
491
190
 
492
- Optional overrides:
191
+ The plugin works well with structured task notes in `memory/tasks/`:
493
192
 
494
- ```bash
495
- # Use a non-default bm binary
496
- BM_BIN=/absolute/path/to/bm bun run test:int
497
-
498
- # Use a specific basic-memory source checkout
499
- BASIC_MEMORY_REPO=/absolute/path/to/basic-memory bun run test:int
500
- ```
193
+ ```markdown
194
+ ---
195
+ title: auth-middleware-rollout
196
+ type: Task
197
+ status: active
198
+ current_step: 2
199
+ ---
501
200
 
502
- ## Development
201
+ ## Context
202
+ Rolling JWT middleware to all API routes.
503
203
 
504
- ```bash
505
- bun run check-types # Type checking
506
- bun run lint # Linting
507
- bun test # Run tests (156 tests)
508
- bun run test:int # Real BM MCP integration tests
204
+ ## Plan
205
+ - [x] Implement middleware
206
+ - [x] Add refresh-token validation
207
+ - [ ] Roll out to staging
208
+ - [ ] Verify logs and error rates
509
209
  ```
510
210
 
511
- ## Publish to npm
211
+ Set `status: done` to mark complete. Done tasks are filtered out of active task results.
512
212
 
513
- This package is published as `@basicmemory/openclaw-basic-memory`.
213
+ ## Basic Memory Cloud
514
214
 
515
- ```bash
516
- # 1) Verify release readiness (types + tests + npm pack dry run)
517
- just release-check
215
+ Everything works locally. Cloud adds cross-device sync, team workspaces, and persistent memory for hosted agents.
518
216
 
519
- # 2) Inspect publish payload
520
- just release-pack
217
+ - Same knowledge graph on laptop, desktop, and CI
218
+ - Shared workspaces for teams
219
+ - Durable memory for production agents
521
220
 
522
- # 3) Authenticate once (if needed)
523
- npm login
221
+ Cloud extends local-first — still plain Markdown, still yours. [Start a free trial](https://basicmemory.com) and use code `BMCLAW` for 20% off for 3 months. See [BASIC_MEMORY.md](./BASIC_MEMORY.md) for setup.
524
222
 
525
- # 4) Publish current version from package.json
526
- just release-publish
527
- ```
223
+ ## Troubleshooting
528
224
 
529
- For a full release (version bump + publish + push tag):
225
+ **`bm` not found** — Install uv, then restart the gateway. Or install manually: `uv tool install basic-memory`
530
226
 
531
- ```bash
532
- just release patch # or: minor, major, 0.2.0, etc.
533
- ```
227
+ **Search returns nothing** — Check that Basic Memory connected (look for `connected to BM` in logs). Verify files exist in the project directory.
534
228
 
535
- ### GitHub Actions CI/CD
229
+ **Jiti cache issues** — `rm -rf /tmp/jiti/ "$TMPDIR/jiti/"` then restart the gateway.
536
230
 
537
- - CI workflow: `.github/workflows/ci.yml` runs on PRs and `main` pushes.
538
- - Release workflow: `.github/workflows/release.yml` runs manually (`workflow_dispatch`) and will:
539
- 1. run release checks
540
- 2. bump version and create a git tag
541
- 3. push commit + tag
542
- 4. publish to npm
543
- 5. create a GitHub release
231
+ **Disable semantic search** — Set `BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=false` to fall back to full-text only.
544
232
 
545
- Publishing uses npm OIDC trusted publishing — no secrets required. The trusted publisher is configured on npmjs.com to accept provenance from this repo's `release.yml` workflow.
233
+ ## More
546
234
 
547
- ### Project Structure
548
- ```
549
- openclaw-basic-memory/
550
- ├── index.ts # Plugin entry — manages MCP lifecycle, registers tools
551
- ├── config.ts # Configuration parsing
552
- ├── bm-client.ts # Persistent Basic Memory MCP stdio client
553
- ├── tools/ # Agent tools
554
- │ ├── search-notes.ts # search_notes
555
- │ ├── read-note.ts # read_note
556
- │ ├── write-note.ts # write_note
557
- │ ├── edit-note.ts # edit_note
558
- │ ├── delete-note.ts # delete_note
559
- │ ├── move-note.ts # move_note
560
- │ ├── build-context.ts # build_context
561
- │ ├── list-memory-projects.ts # list_memory_projects
562
- │ ├── list-workspaces.ts # list_workspaces
563
- │ ├── schema-validate.ts # schema_validate
564
- │ ├── schema-infer.ts # schema_infer
565
- │ ├── schema-diff.ts # schema_diff
566
- │ └── memory-provider.ts # Composited memory_search + memory_get
567
- ├── commands/
568
- │ ├── slash.ts # /remember, /recall
569
- │ ├── skills.ts # /tasks, /reflect, /defrag, /schema
570
- │ └── cli.ts # openclaw basic-memory CLI
571
- └── hooks/
572
- ├── capture.ts # Auto-capture conversations
573
- └── recall.ts # Auto-recall (active tasks + recent activity)
574
- ```
235
+ - [Memory + Task Flow](./MEMORY_TASK_FLOW.md) — practical runbook
236
+ - [Cloud Setup](./BASIC_MEMORY.md) — configure Basic Memory Cloud
237
+ - [Security](./SECURITY.md) — how auto-installation and data handling work
238
+ - [Development](./DEVELOPMENT.md) — contributing, tests, publishing
239
+ - [Basic Memory docs](https://docs.basicmemory.com)
240
+ - [Issues](https://github.com/basicmachines-co/openclaw-basic-memory/issues)
575
241
 
576
242
  ## Telemetry
577
243
 
578
- This plugin collects anonymous, minimal usage events to understand plugin adoption and tool usage patterns. This helps us prioritize features and improve the product.
579
-
580
- **What we collect:**
581
- - Plugin registration and startup events
582
- - First use of each tool per session (deduplicated — not every call)
583
- - Plugin version with each event
584
-
585
- **What we do NOT collect:**
586
- - No file contents, note titles, search queries, or conversation text
587
- - No personally identifiable information (PII)
588
- - No IP address tracking or fingerprinting
589
- - No per-command tracking beyond first-use-per-session
590
-
591
- Events are sent to our [Umami Cloud](https://umami.is) instance, an open-source, privacy-focused analytics platform. Events are fire-and-forget with a 3-second timeout — analytics never blocks or slows the plugin.
592
-
593
- **Opt out** by setting either environment variable:
594
-
595
- ```bash
596
- # Plugin-specific opt-out
597
- export OPENCLAW_BASIC_MEMORY_TELEMETRY=0
598
-
599
- # Or use the shared Basic Memory opt-out (also disables BM CLI telemetry)
600
- export BASIC_MEMORY_NO_PROMOS=1
601
- ```
244
+ This plugin does not collect telemetry. The Basic Memory CLI may send anonymous usage analytics — see the [Basic Memory docs](https://github.com/basicmachines-co/basic-memory) for opt-out instructions.
602
245
 
603
246
  ## License
604
247
 
605
- MIT — see LICENSE file.
606
-
607
- ## Links
608
-
609
- - [Basic Memory](https://github.com/basicmachines-co/basic-memory)
610
- - [Basic Memory Skills](https://github.com/basicmachines-co/basic-memory-skills)
611
- - [OpenClaw](https://docs.openclaw.ai)
612
- - [Issues](https://github.com/basicmachines-co/openclaw-basic-memory/issues)
248
+ MIT
package/bm-client.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { setTimeout as delay } from "node:timers/promises"
2
2
  import { Client } from "@modelcontextprotocol/sdk/client"
3
3
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
4
- import { trackToolCall } from "./analytics.ts"
5
4
  import { log } from "./logger.ts"
6
5
 
7
6
  const DEFAULT_RETRY_DELAYS_MS = [500, 1000, 2000]
@@ -197,7 +196,7 @@ function isRecoverableConnectionError(err: unknown): boolean {
197
196
  )
198
197
  }
199
198
 
200
- function isNoteNotFoundError(err: unknown): boolean {
199
+ function _isNoteNotFoundError(err: unknown): boolean {
201
200
  const msg = getErrorMessage(err).toLowerCase()
202
201
  return (
203
202
  msg.includes("entity not found") ||
@@ -450,7 +449,6 @@ export class BmClient {
450
449
  name: string,
451
450
  args: Record<string, unknown>,
452
451
  ): Promise<unknown> {
453
- trackToolCall(name)
454
452
  const result = await this.callToolRaw(name, args)
455
453
 
456
454
  if (!isRecord(result) || result.structuredContent === undefined) {
@@ -661,11 +659,12 @@ export class BmClient {
661
659
  identifier,
662
660
  operation,
663
661
  content,
664
- find_text: options.find_text,
665
- section: options.section,
666
- expected_replacements: options.expected_replacements,
667
662
  output_format: "json",
668
663
  }
664
+ if (options.find_text) args.find_text = options.find_text
665
+ if (options.section) args.section = options.section
666
+ if (options.expected_replacements != null)
667
+ args.expected_replacements = options.expected_replacements
669
668
  if (project) args.project = project
670
669
 
671
670
  const payload = await this.callTool("edit_note", args)
@@ -831,10 +830,6 @@ export class BmClient {
831
830
  return payload as unknown as MetadataSearchResult
832
831
  }
833
832
 
834
- private isNoteNotFoundError(err: unknown): boolean {
835
- return isNoteNotFoundError(err)
836
- }
837
-
838
833
  async indexConversation(
839
834
  userMessage: string,
840
835
  assistantResponse: string,
@@ -856,22 +851,33 @@ export class BmClient {
856
851
  "---",
857
852
  ].join("\n")
858
853
 
854
+ // Try append first — if it fails for ANY reason, fall through to create
859
855
  try {
860
856
  await this.editNote(title, "append", entry)
861
857
  log.debug(`appended conversation to: ${title}`)
858
+ return
862
859
  } catch (err) {
863
- if (!this.isNoteNotFoundError(err)) {
864
- log.error("conversation append failed", err)
865
- return
866
- }
860
+ log.debug(`append failed, will create: ${getErrorMessage(err)}`)
861
+ }
867
862
 
868
- const content = [`# Conversations ${dateStr}`, "", entry].join("\n")
869
- try {
870
- await this.writeNote(title, content, "conversations")
871
- log.debug(`created conversation note: ${title}`)
872
- } catch (createErr) {
873
- log.error("conversation index failed", createErr)
874
- }
863
+ // Create the note with frontmatter and first entry
864
+ const content = [
865
+ "---",
866
+ `title: Conversations ${dateStr}`,
867
+ "type: Conversation",
868
+ `date: "${dateStr}"`,
869
+ "---",
870
+ "",
871
+ `# Conversations ${dateStr}`,
872
+ "",
873
+ entry,
874
+ ].join("\n")
875
+
876
+ try {
877
+ await this.writeNote(title, content, "conversations")
878
+ log.debug(`created conversation note: ${title}`)
879
+ } catch (err) {
880
+ log.error("conversation index failed", err)
875
881
  }
876
882
  }
877
883
 
package/config.ts CHANGED
@@ -6,11 +6,6 @@ export type CloudConfig = {
6
6
  api_key: string
7
7
  }
8
8
 
9
- export type DashboardConfig = {
10
- enabled: boolean
11
- port: number
12
- }
13
-
14
9
  export type BasicMemoryConfig = {
15
10
  project: string
16
11
  bmPath: string
@@ -23,7 +18,6 @@ export type BasicMemoryConfig = {
23
18
  recallPrompt: string
24
19
  debug: boolean
25
20
  cloud?: CloudConfig
26
- dashboard: DashboardConfig
27
21
  }
28
22
 
29
23
  const ALLOWED_KEYS = [
@@ -43,7 +37,6 @@ const ALLOWED_KEYS = [
43
37
  "recall_prompt",
44
38
  "debug",
45
39
  "cloud",
46
- "dashboard",
47
40
  ]
48
41
 
49
42
  function assertAllowedKeys(
@@ -113,22 +106,6 @@ export function parseConfig(raw: unknown): BasicMemoryConfig {
113
106
  }
114
107
  }
115
108
 
116
- let dashboard: DashboardConfig = { enabled: false, port: 3838 }
117
- if (
118
- cfg.dashboard &&
119
- typeof cfg.dashboard === "object" &&
120
- !Array.isArray(cfg.dashboard)
121
- ) {
122
- const d = cfg.dashboard as Record<string, unknown>
123
- dashboard = {
124
- enabled: typeof d.enabled === "boolean" ? d.enabled : false,
125
- port:
126
- typeof d.port === "number" && d.port > 0 && d.port < 65536
127
- ? d.port
128
- : 3838,
129
- }
130
- }
131
-
132
109
  return {
133
110
  project:
134
111
  typeof cfg.project === "string" && cfg.project.length > 0
@@ -167,7 +144,6 @@ export function parseConfig(raw: unknown): BasicMemoryConfig {
167
144
  : "Check for active tasks and recent activity. Summarize anything relevant to the current session.",
168
145
  debug: typeof cfg.debug === "boolean" ? cfg.debug : false,
169
146
  cloud,
170
- dashboard,
171
147
  }
172
148
  }
173
149
 
package/index.ts CHANGED
@@ -1,10 +1,6 @@
1
- import type { Server } from "node:http"
1
+ import { execSync } from "node:child_process"
2
+
2
3
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
3
- import {
4
- EVENT_PLUGIN_INSTALLED,
5
- EVENT_PLUGIN_STARTED,
6
- track,
7
- } from "./analytics.ts"
8
4
  import { BmClient } from "./bm-client.ts"
9
5
  import { registerCli } from "./commands/cli.ts"
10
6
  import { registerSkillCommands } from "./commands/skills.ts"
@@ -14,10 +10,11 @@ import {
14
10
  parseConfig,
15
11
  resolveProjectPath,
16
12
  } from "./config.ts"
17
- import { createDashboardServer } from "./dashboard/server.ts"
13
+
18
14
  import { buildCaptureHandler } from "./hooks/capture.ts"
19
15
  import { buildRecallHandler } from "./hooks/recall.ts"
20
16
  import { initLogger, log } from "./logger.ts"
17
+ import { CONVERSATION_SCHEMA_CONTENT } from "./schema/conversation-schema.ts"
21
18
  import { TASK_SCHEMA_CONTENT } from "./schema/task-schema.ts"
22
19
  import { registerContextTool } from "./tools/build-context.ts"
23
20
  import { registerDeleteTool } from "./tools/delete-note.ts"
@@ -53,8 +50,6 @@ export default {
53
50
  `project=${cfg.project} memoryDir=${cfg.memoryDir} memoryFile=${cfg.memoryFile}`,
54
51
  )
55
52
 
56
- track(EVENT_PLUGIN_INSTALLED)
57
-
58
53
  const client = new BmClient(cfg.bmPath, cfg.project)
59
54
 
60
55
  // --- BM Tools (always registered) ---
@@ -89,13 +84,47 @@ export default {
89
84
  registerCli(api, client, cfg)
90
85
 
91
86
  // --- Service lifecycle ---
92
- let dashboardServer: Server | undefined
93
-
94
87
  api.registerService({
95
88
  id: "openclaw-basic-memory",
96
89
  start: async (ctx: { config?: unknown; workspaceDir?: string }) => {
97
90
  log.info("starting...")
98
91
 
92
+ // Auto-install bm CLI if not found
93
+ const bmBin = cfg.bmPath || "bm"
94
+ try {
95
+ execSync(`command -v ${bmBin}`, { stdio: "ignore" })
96
+ } catch {
97
+ log.info("bm CLI not found on PATH — attempting auto-install...")
98
+ try {
99
+ execSync("command -v uv", { stdio: "ignore" })
100
+ log.info(
101
+ "installing basic-memory via uv (this may take a minute)...",
102
+ )
103
+ const result = execSync(
104
+ 'uv tool install "basic-memory @ git+https://github.com/basicmachines-co/basic-memory.git@main" --force',
105
+ { encoding: "utf-8", timeout: 120_000, stdio: "pipe" },
106
+ )
107
+ log.info(
108
+ `basic-memory installed: ${result.trim().split("\n").pop()}`,
109
+ )
110
+ // Verify it worked
111
+ try {
112
+ execSync(`command -v ${bmBin}`, { stdio: "ignore" })
113
+ log.info("bm CLI now available on PATH")
114
+ } catch {
115
+ log.error(
116
+ "bm installed but not found on PATH. You may need to add uv's bin directory to your PATH (typically ~/.local/bin).",
117
+ )
118
+ }
119
+ } catch (_uvErr) {
120
+ log.error(
121
+ "Cannot auto-install basic-memory: uv not found. " +
122
+ "Install uv first (brew install uv, or curl -LsSf https://astral.sh/uv/install.sh | sh), " +
123
+ "then restart the gateway.",
124
+ )
125
+ }
126
+ }
127
+
99
128
  const workspace = ctx.workspaceDir ?? process.cwd()
100
129
  const projectPath = resolveProjectPath(cfg.projectPath, workspace)
101
130
  cfg.projectPath = projectPath
@@ -104,46 +133,30 @@ export default {
104
133
  await client.ensureProject(projectPath)
105
134
  log.debug(`project "${cfg.project}" at ${projectPath}`)
106
135
 
107
- // Seed Task schema if not already present
108
- try {
109
- await client.readNote("schema/Task")
110
- log.debug("Task schema already exists, skipping seed")
111
- } catch {
136
+ // Seed schemas if not already present
137
+ for (const [name, content] of [
138
+ ["Task", TASK_SCHEMA_CONTENT],
139
+ ["Conversation", CONVERSATION_SCHEMA_CONTENT],
140
+ ] as const) {
112
141
  try {
113
- await client.writeNote("Task", TASK_SCHEMA_CONTENT, "schema")
114
- log.debug("seeded Task schema note")
115
- } catch (err) {
116
- log.debug("Task schema seed failed (non-fatal)", err)
142
+ await client.readNote(`schema/${name}`)
143
+ log.debug(`${name} schema already exists, skipping seed`)
144
+ } catch {
145
+ try {
146
+ await client.writeNote(name, content, "schema")
147
+ log.debug(`seeded ${name} schema note`)
148
+ } catch (err) {
149
+ log.debug(`${name} schema seed failed (non-fatal)`, err)
150
+ }
117
151
  }
118
152
  }
119
153
 
120
154
  setWorkspaceDir(workspace)
121
155
 
122
- // Start dashboard if enabled
123
- if (cfg.dashboard.enabled) {
124
- dashboardServer = createDashboardServer({
125
- port: cfg.dashboard.port,
126
- client,
127
- })
128
- dashboardServer.listen(cfg.dashboard.port, () => {
129
- log.info(
130
- `dashboard running at http://localhost:${cfg.dashboard.port}`,
131
- )
132
- })
133
- }
134
-
135
- track(EVENT_PLUGIN_STARTED, { project: cfg.project })
136
156
  log.info("connected — BM MCP stdio session running")
137
157
  },
138
158
  stop: async () => {
139
159
  log.info("stopping BM MCP session...")
140
- if (dashboardServer) {
141
- await new Promise<void>((resolve) =>
142
- dashboardServer?.close(() => resolve()),
143
- )
144
- dashboardServer = undefined
145
- log.info("dashboard stopped")
146
- }
147
160
  await client.stop()
148
161
  log.info("stopped")
149
162
  },
@@ -56,11 +56,6 @@
56
56
  "label": "Cloud Backend",
57
57
  "help": "Optional cloud backend config (url + api_key). If present, uses cloud instead of local BM.",
58
58
  "advanced": true
59
- },
60
- "dashboard": {
61
- "label": "Dashboard",
62
- "help": "Web dashboard for visualizing the knowledge graph (enabled, port)",
63
- "advanced": true
64
59
  }
65
60
  },
66
61
  "configSchema": {
@@ -81,13 +76,6 @@
81
76
  "url": { "type": "string" },
82
77
  "api_key": { "type": "string" }
83
78
  }
84
- },
85
- "dashboard": {
86
- "type": "object",
87
- "properties": {
88
- "enabled": { "type": "boolean" },
89
- "port": { "type": "number", "minimum": 1, "maximum": 65535 }
90
- }
91
79
  }
92
80
  },
93
81
  "required": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basicmemory/openclaw-basic-memory",
3
- "version": "0.1.0-alpha.9",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Basic Memory plugin for OpenClaw — local-first knowledge graph for agent memory",
6
6
  "license": "MIT",
@@ -9,7 +9,6 @@
9
9
  "url": "https://github.com/basicmachines-co/openclaw-basic-memory"
10
10
  },
11
11
  "files": [
12
- "analytics.ts",
13
12
  "index.ts",
14
13
  "bm-client.ts",
15
14
  "config.ts",
@@ -34,6 +33,7 @@
34
33
  "tools/write-note.ts",
35
34
  "types/openclaw.d.ts",
36
35
  "schema/task-schema.ts",
36
+ "schema/conversation-schema.ts",
37
37
  "skills/",
38
38
  "scripts/setup-bm.sh",
39
39
  "openclaw.plugin.json",
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Canonical Conversation schema note content, seeded into new projects on first startup.
3
+ * If the schema already exists (user may have customized it), seeding is skipped.
4
+ */
5
+ export const CONVERSATION_SCHEMA_CONTENT = `---
6
+ title: Conversation
7
+ type: schema
8
+ entity: Conversation
9
+ version: 1
10
+ schema:
11
+ date: "string, ISO date YYYY-MM-DD"
12
+ session_id?: "string, unique session identifier"
13
+ channel?: "string, where the conversation happened (webchat, telegram, discord, etc)"
14
+ participants?: "array, who was in the conversation"
15
+ topic?: "string, brief description of main topic"
16
+ summary?: "string, one-paragraph summary of key points"
17
+ key_decisions?: "array, decisions made during conversation"
18
+ action_items?: "array, things to do as a result"
19
+ settings:
20
+ validation: warn
21
+ ---
22
+
23
+ # Conversation
24
+
25
+ A record of a conversation session between the agent and user(s).
26
+
27
+ ## Observations
28
+ - [convention] Conversation files live in memory/conversations/ with format conversations-YYYY-MM-DD.md
29
+ - [convention] Messages are appended as the conversation progresses
30
+ - [convention] Summary and key_decisions populated at session end or by memory-reflect
31
+ - [convention] Skip routine greetings, heartbeat acks, and tool call details
32
+ - [convention] Focus on decisions, actions taken, and key context needed for continuity
33
+ `
package/analytics.ts DELETED
@@ -1,162 +0,0 @@
1
- /**
2
- * Lightweight analytics via Umami event collector.
3
- *
4
- * Sends anonymous, non-blocking usage events to help understand plugin adoption
5
- * and tool usage patterns. No PII, no fingerprinting, no cookies.
6
- *
7
- * Events are fire-and-forget — analytics never blocks or breaks the plugin.
8
- *
9
- * Defaults point to the Basic Memory Umami Cloud instance. Override via:
10
- * BASIC_MEMORY_UMAMI_HOST — Custom Umami instance URL
11
- * BASIC_MEMORY_UMAMI_SITE_ID — Custom Website ID
12
- * Opt out entirely with OPENCLAW_BASIC_MEMORY_TELEMETRY=0 or BASIC_MEMORY_NO_PROMOS=1.
13
- */
14
-
15
- import { readFileSync } from "node:fs"
16
- import { join } from "node:path"
17
-
18
- // ---------------------------------------------------------------------------
19
- // Configuration
20
- // ---------------------------------------------------------------------------
21
-
22
- const DEFAULT_UMAMI_HOST = "https://cloud.umami.is"
23
- const DEFAULT_UMAMI_SITE_ID = "f6479898-ebaf-4e60-bce2-6dc60a3f6c5c"
24
- const SEND_TIMEOUT_MS = 3000
25
-
26
- function umamiHost(): string {
27
- return process.env.BASIC_MEMORY_UMAMI_HOST?.trim() || DEFAULT_UMAMI_HOST
28
- }
29
-
30
- function umamiSiteId(): string {
31
- return process.env.BASIC_MEMORY_UMAMI_SITE_ID?.trim() || DEFAULT_UMAMI_SITE_ID
32
- }
33
-
34
- export function analyticsDisabled(): boolean {
35
- // Plugin-specific opt-out
36
- const telemetry = (process.env.OPENCLAW_BASIC_MEMORY_TELEMETRY ?? "")
37
- .trim()
38
- .toLowerCase()
39
- if (telemetry === "0" || telemetry === "false" || telemetry === "no") {
40
- return true
41
- }
42
-
43
- // Shared Basic Memory opt-out (also disables BM CLI analytics)
44
- const noPromos = (process.env.BASIC_MEMORY_NO_PROMOS ?? "")
45
- .trim()
46
- .toLowerCase()
47
- return noPromos === "1" || noPromos === "true" || noPromos === "yes"
48
- }
49
-
50
- // ---------------------------------------------------------------------------
51
- // Version
52
- // ---------------------------------------------------------------------------
53
-
54
- let _cachedVersion: string | null = null
55
-
56
- function getVersion(): string {
57
- if (_cachedVersion) return _cachedVersion
58
- try {
59
- const pkg = JSON.parse(
60
- readFileSync(
61
- join(import.meta.dirname ?? __dirname, "package.json"),
62
- "utf-8",
63
- ),
64
- )
65
- _cachedVersion = pkg.version ?? "unknown"
66
- } catch {
67
- _cachedVersion = "unknown"
68
- }
69
- return _cachedVersion ?? "unknown"
70
- }
71
-
72
- // ---------------------------------------------------------------------------
73
- // Session-scoped dedup for tool calls
74
- // ---------------------------------------------------------------------------
75
-
76
- const _trackedTools = new Set<string>()
77
-
78
- export function resetTrackedTools(): void {
79
- _trackedTools.clear()
80
- }
81
-
82
- // ---------------------------------------------------------------------------
83
- // Event constants
84
- // ---------------------------------------------------------------------------
85
-
86
- export const EVENT_PLUGIN_INSTALLED = "openclaw-plugin-installed"
87
- export const EVENT_PLUGIN_STARTED = "openclaw-plugin-started"
88
- export const EVENT_TOOL_CALL = "openclaw-tool-call"
89
-
90
- // ---------------------------------------------------------------------------
91
- // Public API
92
- // ---------------------------------------------------------------------------
93
-
94
- /**
95
- * Send an analytics event to Umami. Non-blocking, silent on failure.
96
- *
97
- * @param eventName - Short kebab-case name (e.g. "openclaw-plugin-started")
98
- * @param data - Optional dict of event properties (string/number values)
99
- */
100
- export function track(
101
- eventName: string,
102
- data?: Record<string, string | number>,
103
- ): void {
104
- if (analyticsDisabled()) return
105
-
106
- const host = umamiHost()
107
- const siteId = umamiSiteId()
108
-
109
- const payload = {
110
- payload: {
111
- hostname: "openclaw.basicmemory.com",
112
- language: "en",
113
- url: `/openclaw/${eventName}`,
114
- website: siteId,
115
- name: eventName,
116
- data: {
117
- version: getVersion(),
118
- ...(data ?? {}),
119
- },
120
- },
121
- }
122
-
123
- // Fire-and-forget — never block the plugin
124
- _sendPayload(host, payload).catch(() => {})
125
- }
126
-
127
- /**
128
- * Track a tool call, deduped per tool name per session.
129
- * Only the first invocation of each tool sends an event.
130
- */
131
- export function trackToolCall(toolName: string): void {
132
- if (analyticsDisabled()) return
133
- if (_trackedTools.has(toolName)) return
134
- _trackedTools.add(toolName)
135
- track(EVENT_TOOL_CALL, { tool: toolName })
136
- }
137
-
138
- // ---------------------------------------------------------------------------
139
- // Internal send (exported for testing)
140
- // ---------------------------------------------------------------------------
141
-
142
- export async function _sendPayload(
143
- host: string,
144
- payload: unknown,
145
- ): Promise<void> {
146
- const controller = new AbortController()
147
- const timer = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS)
148
-
149
- try {
150
- await fetch(`${host}/api/send`, {
151
- method: "POST",
152
- headers: {
153
- "Content-Type": "application/json",
154
- "User-Agent": `openclaw-basic-memory/${getVersion()}`,
155
- },
156
- body: JSON.stringify(payload),
157
- signal: controller.signal,
158
- })
159
- } finally {
160
- clearTimeout(timer)
161
- }
162
- }