@xdarkicex/openclaw-memory-libravdb 1.4.2 → 1.4.4
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 +76 -16
- package/docs/README.md +3 -12
- package/docs/architecture.md +68 -153
- package/docs/contributing.md +1 -2
- package/docs/embedding-profiles.md +5 -6
- package/docs/installation.md +1 -1
- package/openclaw.plugin.json +65 -2
- package/package.json +2 -2
- package/src/cli.ts +34 -0
- package/src/comparison-experiments.ts +128 -0
- package/src/context-engine.ts +276 -62
- package/src/dream-promotion.ts +492 -0
- package/src/dream-routing.ts +40 -0
- package/src/index.ts +16 -1
- package/src/markdown-hash.ts +104 -0
- package/src/markdown-ingest.ts +627 -0
- package/src/memory-runtime.ts +32 -9
- package/src/scoring.ts +6 -3
- package/src/temporal.ts +657 -80
- package/src/types.ts +48 -0
- package/docs/ast-v2.md +0 -167
- package/docs/ast.md +0 -70
- package/docs/compaction-evaluation.md +0 -182
- package/docs/continuity.md +0 -708
- package/docs/elevated-guidance.md +0 -258
- package/docs/gating.md +0 -134
- package/docs/implementation.md +0 -447
- package/docs/mathematics-v2.md +0 -1879
- package/docs/mathematics.md +0 -695
package/README.md
CHANGED
|
@@ -104,6 +104,78 @@ Expected healthy state:
|
|
|
104
104
|
- the plugin is active as the memory provider
|
|
105
105
|
- the runtime can report stored counts and model readiness
|
|
106
106
|
|
|
107
|
+
## Markdown Ingestion
|
|
108
|
+
|
|
109
|
+
LibraVDB Memory can watch markdown roots and sync changed notes into vector
|
|
110
|
+
memory without changing the Go sidecar RPC contract.
|
|
111
|
+
|
|
112
|
+
The two built-in source adapters are:
|
|
113
|
+
|
|
114
|
+
- `generic`: for OpenClaw-owned markdown, including stock files like
|
|
115
|
+
`MEMORY.md`
|
|
116
|
+
- `obsidian`: for Obsidian vault roots, with tag-aware defaults
|
|
117
|
+
|
|
118
|
+
Configuration is driven through the plugin config fields in
|
|
119
|
+
`openclaw.plugin.json`:
|
|
120
|
+
|
|
121
|
+
- `markdownIngestionEnabled`
|
|
122
|
+
- `markdownIngestionRoots`
|
|
123
|
+
- `markdownIngestionInclude`
|
|
124
|
+
- `markdownIngestionExclude`
|
|
125
|
+
- `markdownIngestionDebounceMs`
|
|
126
|
+
- `markdownIngestionObsidianEnabled`
|
|
127
|
+
- `markdownIngestionObsidianRoots`
|
|
128
|
+
- `markdownIngestionObsidianInclude`
|
|
129
|
+
- `markdownIngestionObsidianExclude`
|
|
130
|
+
- `markdownIngestionObsidianDebounceMs`
|
|
131
|
+
|
|
132
|
+
Typical usage:
|
|
133
|
+
|
|
134
|
+
- point `markdownIngestionRoots` at OpenClaw-owned markdown roots, such as
|
|
135
|
+
`.openclaw/skills/*/*.md` or a stock memory directory that contains
|
|
136
|
+
`MEMORY.md`
|
|
137
|
+
- enable the Obsidian adapter separately with
|
|
138
|
+
`markdownIngestionObsidianEnabled: true` and one or more vault roots
|
|
139
|
+
- use include/exclude globs to narrow what gets watched when needed
|
|
140
|
+
|
|
141
|
+
By default, the Obsidian adapter only auto-ingests notes that look like memory
|
|
142
|
+
notes, using frontmatter tags or inline tags like `#project`. The OpenClaw
|
|
143
|
+
stock `MEMORY.md` file is always eligible through the generic adapter path.
|
|
144
|
+
|
|
145
|
+
## Dream Promotion
|
|
146
|
+
|
|
147
|
+
Dream promotion is a separate, opt-in path for promoting vetted dream diary
|
|
148
|
+
entries directly into a dedicated `dream:{userId}` collection.
|
|
149
|
+
|
|
150
|
+
It does not use `MEMORY.md`. Instead, it expects a dream diary markdown file
|
|
151
|
+
that contains explicit candidate bullets under promotion-oriented headings such
|
|
152
|
+
as `## Deep Sleep`. Each promoted bullet should include a trailing metadata
|
|
153
|
+
block with the gating fields:
|
|
154
|
+
|
|
155
|
+
```md
|
|
156
|
+
- Preserve the recent tail buffer {score=0.82 recall=3 unique=2}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Only bullets that satisfy the sidecar gates are inserted. The dream collection
|
|
160
|
+
is isolated from normal `user:` and `global` retrieval by default, and dream
|
|
161
|
+
phrasing in chat or search queries routes there automatically.
|
|
162
|
+
|
|
163
|
+
Configure automatic diary watching with:
|
|
164
|
+
|
|
165
|
+
- `dreamPromotionEnabled`
|
|
166
|
+
- `dreamPromotionDiaryPath`
|
|
167
|
+
- `dreamPromotionUserId`
|
|
168
|
+
- `dreamPromotionDebounceMs`
|
|
169
|
+
|
|
170
|
+
For a manual run, use:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
openclaw memory dream-promote --user-id <userId> --dream-file /path/to/DREAMS.md
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The manual command and the automatic watcher both go through the same sidecar
|
|
177
|
+
promotion RPC, so the admission gates and provenance metadata are identical.
|
|
178
|
+
|
|
107
179
|
## Install Model
|
|
108
180
|
|
|
109
181
|
This plugin is intentionally **connect-only** at install time.
|
|
@@ -155,8 +227,7 @@ The main runtime split is:
|
|
|
155
227
|
- compaction and summarization
|
|
156
228
|
- stable local IPC endpoint
|
|
157
229
|
|
|
158
|
-
For the implemented architecture map, read
|
|
159
|
-
[docs/architecture.md](./docs/architecture.md).
|
|
230
|
+
For the implemented architecture map, read [docs/architecture.md](./docs/architecture.md).
|
|
160
231
|
|
|
161
232
|
## Retrieval Model
|
|
162
233
|
|
|
@@ -178,12 +249,8 @@ The ranking model currently blends:
|
|
|
178
249
|
- recency decay
|
|
179
250
|
- summary quality attenuation
|
|
180
251
|
|
|
181
|
-
The formal math
|
|
182
|
-
|
|
183
|
-
- [docs/mathematics-v2.md](./docs/mathematics-v2.md)
|
|
184
|
-
- [docs/continuity.md](./docs/continuity.md)
|
|
185
|
-
- [docs/ast-v2.md](./docs/ast-v2.md)
|
|
186
|
-
- [docs/elevated-guidance.md](./docs/elevated-guidance.md)
|
|
252
|
+
The formal math and deeper design notes are kept out of the public index on
|
|
253
|
+
purpose.
|
|
187
254
|
|
|
188
255
|
## LongMemEval Harness
|
|
189
256
|
|
|
@@ -273,7 +340,7 @@ Good entry points:
|
|
|
273
340
|
- [docs/install.md](./docs/install.md)
|
|
274
341
|
- [docs/installation.md](./docs/installation.md)
|
|
275
342
|
- [docs/uninstall.md](./docs/uninstall.md)
|
|
276
|
-
- [docs/
|
|
343
|
+
- [docs/architecture.md](./docs/architecture.md)
|
|
277
344
|
|
|
278
345
|
## For Researchers And Builders
|
|
279
346
|
|
|
@@ -293,9 +360,6 @@ Start here:
|
|
|
293
360
|
|
|
294
361
|
- [docs/problem.md](./docs/problem.md)
|
|
295
362
|
- [docs/architecture.md](./docs/architecture.md)
|
|
296
|
-
- [docs/mathematics-v2.md](./docs/mathematics-v2.md)
|
|
297
|
-
- [docs/gating.md](./docs/gating.md)
|
|
298
|
-
- [docs/continuity.md](./docs/continuity.md)
|
|
299
363
|
|
|
300
364
|
## Runtime Facts
|
|
301
365
|
|
|
@@ -316,10 +380,6 @@ Start here:
|
|
|
316
380
|
- [docs/uninstall.md](./docs/uninstall.md): clean shutdown and removal
|
|
317
381
|
- [docs/architecture.md](./docs/architecture.md): current implemented system
|
|
318
382
|
architecture
|
|
319
|
-
- [docs/implementation.md](./docs/implementation.md): important implementation
|
|
320
|
-
contracts
|
|
321
|
-
- [docs/mathematics-v2.md](./docs/mathematics-v2.md): formal scoring and
|
|
322
|
-
optimization reference
|
|
323
383
|
|
|
324
384
|
## Current Constraint
|
|
325
385
|
|
package/docs/README.md
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
# Documentation Index
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
to preserve project history and design evolution.
|
|
3
|
+
This index lists the public docs that remain in the main repo. Detailed math
|
|
4
|
+
and private design notes are kept out of the public index on purpose.
|
|
6
5
|
|
|
7
6
|
- [installation.md](./installation.md) - Complete install, activation, verification, and troubleshooting reference.
|
|
8
7
|
- [install.md](./install.md) - Practical lifecycle guide for Homebrew, the OpenClaw plugin, and manual daemon management.
|
|
9
8
|
- [uninstall.md](./uninstall.md) - Clean shutdown and removal guide for the plugin, daemon, and optional local data.
|
|
10
|
-
- [architecture.md](./architecture.md) -
|
|
9
|
+
- [architecture.md](./architecture.md) - Public system overview covering the plugin, daemon, storage, retrieval, and compaction flow.
|
|
11
10
|
- [problem.md](./problem.md) - Technical argument for replacing the stock OpenClaw memory lifecycle in this use case.
|
|
12
|
-
- [mathematics-v2.md](./mathematics-v2.md) - Formal reference for hybrid scoring, decay, token budgeting, Matryoshka retrieval, compaction, planned two-pass retrieval, and temporal-compositional projection.
|
|
13
|
-
- [compaction-evaluation.md](./compaction-evaluation.md) - Real-model benchmark notes for T5 summary confidence, Nomic-space preservation, and the hard preservation gate.
|
|
14
|
-
- [continuity.md](./continuity.md) - Continuity model for invariant context, preserved recent raw session tail, and retrieved older memory.
|
|
15
|
-
- [ast-v2.md](./ast-v2.md) - Reviewed authoritative AST partitioning reference for authored Markdown hard invariants, soft invariants, and variant lore.
|
|
16
|
-
- [elevated-guidance.md](./elevated-guidance.md) - Tier 1.5 protected-shard and elevated-guidance model for preserving shadow rules through compaction.
|
|
17
|
-
- [ast.md](./ast.md) - Historical predecessor to `ast-v2.md`, kept to show design evolution and earlier bugs.
|
|
18
|
-
- [gating.md](./gating.md) - Full derivation and calibration guide for the domain-adaptive gating scalar.
|
|
19
|
-
- [implementation.md](./implementation.md) - Non-obvious implementation decisions and their rationale.
|
|
20
11
|
- [dependencies.md](./dependencies.md) - Why LibraVDB and slab-based storage were chosen for this plugin.
|
|
21
12
|
- [models.md](./models.md) - ONNX model strategy, latency trade-offs, and shipped model roles.
|
|
22
13
|
- [security.md](./security.md) - Security model, untrusted-memory framing, isolation guarantees, and deletion boundaries.
|
package/docs/architecture.md
CHANGED
|
@@ -1,182 +1,97 @@
|
|
|
1
1
|
# System Architecture
|
|
2
2
|
|
|
3
|
-
This document describes the current implemented architecture
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
This document describes the current implemented architecture at a public,
|
|
4
|
+
operational level. It intentionally avoids implementation anchors and detailed
|
|
5
|
+
internal sequencing.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
LibraVDB Memory is split into two cooperating pieces:
|
|
10
|
+
|
|
11
|
+
- a TypeScript OpenClaw plugin that owns the `memory` and `contextEngine`
|
|
12
|
+
slots
|
|
13
|
+
- a Go sidecar daemon that owns storage, retrieval, and compaction
|
|
14
|
+
|
|
15
|
+
The plugin keeps the host integration light and stable. The daemon keeps the
|
|
16
|
+
data path local-first and handles the expensive memory operations outside the
|
|
17
|
+
main chat process.
|
|
18
|
+
|
|
19
|
+
## Component Map
|
|
8
20
|
|
|
9
21
|
```mermaid
|
|
10
22
|
flowchart LR
|
|
11
|
-
Host["OpenClaw host
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Sidecar["Go daemon
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Extractive["Extractive summarizer"]
|
|
25
|
-
T5["Optional ONNX T5 summarizer"]
|
|
26
|
-
Ollama["Optional Ollama summarizer endpoint"]
|
|
27
|
-
|
|
28
|
-
Host --> CE
|
|
29
|
-
Host --> MPS
|
|
30
|
-
CE --> Runtime
|
|
31
|
-
Runtime --> RPC
|
|
32
|
-
RPC --> Sidecar
|
|
23
|
+
Host["OpenClaw host"]
|
|
24
|
+
Plugin["TypeScript plugin\nregistration + context engine"]
|
|
25
|
+
Runtime["Plugin runtime\nlazy daemon connect + RPC"]
|
|
26
|
+
MPS["memoryPromptSection\ncapability header"]
|
|
27
|
+
Sidecar["Go daemon"]
|
|
28
|
+
Store["LibraVDB store"]
|
|
29
|
+
Embed["Embedding engine"]
|
|
30
|
+
Summarizer["Summarizer(s)"]
|
|
31
|
+
|
|
32
|
+
Host --> Plugin
|
|
33
|
+
Plugin --> MPS
|
|
34
|
+
Plugin --> Runtime
|
|
35
|
+
Runtime --> Sidecar
|
|
33
36
|
Sidecar --> Embed
|
|
34
|
-
Sidecar -->
|
|
35
|
-
Sidecar --> T5
|
|
36
|
-
Sidecar --> Ollama
|
|
37
|
+
Sidecar --> Summarizer
|
|
37
38
|
Sidecar --> Store
|
|
38
|
-
Store --> Session
|
|
39
|
-
Store --> Turns
|
|
40
|
-
Store --> User
|
|
41
|
-
Store --> Global
|
|
42
|
-
Store --> Dirty
|
|
43
39
|
```
|
|
44
40
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- plugin entry: [`src/index.ts`](../src/index.ts)
|
|
48
|
-
- lazy runtime startup: [`src/plugin-runtime.ts`](../src/plugin-runtime.ts)
|
|
49
|
-
- daemon supervision and endpoint discovery: [`src/sidecar.ts`](../src/sidecar.ts)
|
|
50
|
-
- transport listener: [`sidecar/server/transport.go`](../sidecar/server/transport.go)
|
|
51
|
-
- RPC method table: [`sidecar/server/rpc.go`](../sidecar/server/rpc.go)
|
|
52
|
-
- store: [`sidecar/store/libravdb.go`](../sidecar/store/libravdb.go)
|
|
53
|
-
|
|
54
|
-
## 2. Single-Turn Data Flow
|
|
55
|
-
|
|
56
|
-
### 2.1 `ingest`
|
|
57
|
-
|
|
58
|
-
Implemented in [`src/context-engine.ts`](../src/context-engine.ts).
|
|
59
|
-
|
|
60
|
-
For every non-heartbeat message:
|
|
61
|
-
|
|
62
|
-
1. The host gets an RPC client from the plugin runtime. This lazily connects to
|
|
63
|
-
the configured daemon endpoint when the plugin is first used.
|
|
64
|
-
2. The message is written to `session:<sessionId>` with `type: "turn"`.
|
|
65
|
-
3. If `message.role === "user"`, the same text is written to `turns:<userId>`.
|
|
66
|
-
4. The host calls `gating_scalar` with `{ userId, text }`.
|
|
67
|
-
5. If `g >= ingestionGateThreshold`, the turn is promoted into
|
|
68
|
-
`user:<userId>` with the full gating decomposition in metadata.
|
|
69
|
-
|
|
70
|
-
Important constraints from the current implementation:
|
|
71
|
-
|
|
72
|
-
- session insertion is fire-and-forget
|
|
73
|
-
- durable promotion is best-effort
|
|
74
|
-
- gating failure does not fail the user turn
|
|
75
|
-
- assistant turns are stored in session memory but are not promoted into
|
|
76
|
-
durable user memory
|
|
77
|
-
|
|
78
|
-
### 2.2 `memoryPromptSection`
|
|
79
|
-
|
|
80
|
-
Implemented in [`src/memory-provider.ts`](../src/memory-provider.ts).
|
|
81
|
-
|
|
82
|
-
Before the main assembly path runs, the plugin returns a lightweight static
|
|
83
|
-
header fragment that tells the host persistent memory is active.
|
|
84
|
-
|
|
85
|
-
This path is intentionally synchronous and does not perform RPC retrieval.
|
|
86
|
-
Durable recall now happens entirely inside `assemble`, which keeps embedded
|
|
87
|
-
prompt construction compatible with OpenClaw's synchronous memory prompt hook.
|
|
88
|
-
|
|
89
|
-
### 2.3 `assemble`
|
|
90
|
-
|
|
91
|
-
Implemented in [`src/context-engine.ts`](../src/context-engine.ts).
|
|
92
|
-
|
|
93
|
-
For the current query text (last message content), the host:
|
|
94
|
-
|
|
95
|
-
1. builds an exclusion set from the most recent message ids
|
|
96
|
-
2. searches `session:<sessionId>`, `user:<userId>`, and `global` in parallel
|
|
97
|
-
3. hybrid-ranks the combined results using host-side scoring
|
|
98
|
-
4. fits the ranked set to `tokenBudget * tokenBudgetFraction`
|
|
99
|
-
5. prepends the selected memories as synthetic `system` messages
|
|
100
|
-
6. returns both the expanded message array and a `systemPromptAddition`
|
|
41
|
+
## Runtime Flow
|
|
101
42
|
|
|
102
|
-
|
|
43
|
+
### `memoryPromptSection`
|
|
103
44
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- `assemble` does not mutate the original `messages` array in place; it returns
|
|
107
|
-
a new array
|
|
45
|
+
The memory prompt hook returns a small static capability header. It is not the
|
|
46
|
+
main retrieval path.
|
|
108
47
|
|
|
109
|
-
|
|
48
|
+
### `ingest`
|
|
110
49
|
|
|
111
|
-
|
|
112
|
-
|
|
50
|
+
Session messages are written into the sidecar-backed store. User turns may also
|
|
51
|
+
be promoted into durable user memory after gating.
|
|
113
52
|
|
|
114
|
-
|
|
53
|
+
### `assemble`
|
|
115
54
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
chronological clusters
|
|
120
|
-
4. each cluster is routed to:
|
|
121
|
-
- extractive summarization by default
|
|
122
|
-
- optional abstractive summarization if `mean(gating_score) >= 0.60` and an
|
|
123
|
-
abstractive summarizer is ready
|
|
124
|
-
5. the summary record is inserted back into the same session collection
|
|
125
|
-
6. source turns are deleted only after summary insertion succeeds
|
|
55
|
+
The context engine queries the relevant memory scopes, ranks the results, fits
|
|
56
|
+
them to the current token budget, and injects the selected items as synthetic
|
|
57
|
+
system messages.
|
|
126
58
|
|
|
127
|
-
|
|
59
|
+
### `compact`
|
|
128
60
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
61
|
+
Compaction is explicit rather than background-only. The host can request a
|
|
62
|
+
compaction pass, and the system then decides whether the current session is
|
|
63
|
+
eligible. When it does compact, it preserves the newest working context, turns
|
|
64
|
+
older content into smaller summaries, and keeps the raw recent tail intact.
|
|
133
65
|
|
|
134
|
-
|
|
66
|
+
If the input is too small or the compactable portion does not clear local
|
|
67
|
+
thresholds, compaction declines instead of forcing a rewrite.
|
|
135
68
|
|
|
136
|
-
|
|
137
|
-
from the original spec phrasing.
|
|
69
|
+
## Current Boundaries
|
|
138
70
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
| `assemble` RPC failure | returns original messages, original token count, and empty `systemPromptAddition` | That turn gets no recall augmentation |
|
|
145
|
-
| `ingest` gating or durable insert failure | session write already happened; durable promotion is skipped | Session memory survives, durable memory may miss that turn |
|
|
146
|
-
| Compaction summarizer unavailable | extractive summarizer remains required; optional abstractive path is skipped | Compaction still runs extractively when extractive is healthy |
|
|
147
|
-
| Disk full or insert error | Go RPC returns an error; TypeScript caller logs or degrades | New records are not stored, but chat continues |
|
|
148
|
-
| Empty lower Matryoshka tiers | cascade search naturally falls through because empty tiers return `best = 0.0` | Retrieval degrades to higher tiers without returning false confident exits |
|
|
71
|
+
- `memoryPromptSection` stays lightweight
|
|
72
|
+
- retrieval happens in `assemble`
|
|
73
|
+
- compaction is separate from prompt construction
|
|
74
|
+
- lifecycle hints such as `before_reset` and `session_end` are advisory
|
|
75
|
+
- the sidecar is the source of truth for stored memory state
|
|
149
76
|
|
|
150
|
-
|
|
77
|
+
## Failure Handling
|
|
151
78
|
|
|
152
|
-
|
|
153
|
-
- lazy daemon connect and health gate: [`src/plugin-runtime.ts`](../src/plugin-runtime.ts)
|
|
154
|
-
- compaction routing and insert/delete ordering:
|
|
155
|
-
[`sidecar/compact/summarize.go`](../sidecar/compact/summarize.go)
|
|
79
|
+
The plugin is designed to degrade gracefully:
|
|
156
80
|
|
|
157
|
-
|
|
81
|
+
- if the daemon is unavailable, prompt assembly continues without recall
|
|
82
|
+
- if compaction fails, the active session is not blocked
|
|
83
|
+
- if summarization is unavailable, the system falls back to the safer path
|
|
158
84
|
|
|
159
|
-
|
|
85
|
+
That keeps the chat usable even when the memory backend is temporarily down.
|
|
160
86
|
|
|
161
|
-
|
|
162
|
-
2. the host calls `gating_scalar`
|
|
163
|
-
3. the Go daemon performs exactly two searches:
|
|
164
|
-
- `SearchText("turns:<userId>", text, 10, nil)`
|
|
165
|
-
- `SearchText("user:<userId>", text, 5, nil)`
|
|
166
|
-
4. the daemon computes `GatingSignals` with [`compact.ComputeGating`](../sidecar/compact/gate.go)
|
|
167
|
-
5. the host compares `g` to `ingestionGateThreshold`
|
|
168
|
-
6. on pass, the host writes the turn into `user:<userId>` with all gating
|
|
169
|
-
metadata fields
|
|
170
|
-
7. later, compaction computes the mean `gating_score` of a cluster and may route
|
|
171
|
-
high-value clusters to the abstractive summarizer
|
|
87
|
+
## Why This Shape
|
|
172
88
|
|
|
173
|
-
|
|
89
|
+
This architecture keeps the host integration simple while still supporting:
|
|
174
90
|
|
|
175
|
-
-
|
|
176
|
-
-
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
gating metadata because it was never promoted
|
|
91
|
+
- separate session, durable-user, and global scopes
|
|
92
|
+
- bounded prompt assembly
|
|
93
|
+
- explicit compaction
|
|
94
|
+
- local-first storage and retrieval
|
|
180
95
|
|
|
181
|
-
|
|
182
|
-
|
|
96
|
+
In short, the plugin owns the lifecycle contract, and the sidecar owns the
|
|
97
|
+
heavy lifting.
|
package/docs/contributing.md
CHANGED
|
@@ -73,8 +73,7 @@ Before opening a PR:
|
|
|
73
73
|
- `pnpm check` must pass
|
|
74
74
|
- `go test -race ./...` from `sidecar/` must pass
|
|
75
75
|
- any new gating signal must come with calibration or invariant coverage
|
|
76
|
-
- any retrieval math change must be reflected in
|
|
77
|
-
- any gating change must be reflected in [gating.md](./gating.md)
|
|
76
|
+
- any retrieval math or gating change must be reflected in the private design notes
|
|
78
77
|
|
|
79
78
|
## Release Versioning
|
|
80
79
|
|
|
@@ -4,15 +4,14 @@ The plugin now supports a lightweight `embeddingProfile` setting for named local
|
|
|
4
4
|
|
|
5
5
|
Default selection baseline as of `2026-03-28`:
|
|
6
6
|
|
|
7
|
-
- default embedding profile: `
|
|
7
|
+
- default embedding profile: `all-minilm-l6-v2`
|
|
8
8
|
- bundled fallback profile: `all-minilm-l6-v2`
|
|
9
9
|
|
|
10
10
|
Why:
|
|
11
11
|
|
|
12
|
-
- MiniLM
|
|
13
|
-
- Nomic
|
|
14
|
-
- Nomic
|
|
15
|
-
- Adversarial lexical traps remain reranker-window cases, but Nomic still narrows the relevant-vs-distractor margin materially.
|
|
12
|
+
- MiniLM keeps the local LongMemEval retrieval slice inside the target memory envelope on macOS.
|
|
13
|
+
- Nomic remains available as an explicit opt-in profile for long-context experiments.
|
|
14
|
+
- Nomic ONNX on macOS is fragile with CoreML execution and can trigger multi-GB RSS, so it is no longer the safe bundled default.
|
|
16
15
|
|
|
17
16
|
Current shipped profile names:
|
|
18
17
|
|
|
@@ -37,6 +36,6 @@ How it works:
|
|
|
37
36
|
|
|
38
37
|
Recommended usage:
|
|
39
38
|
|
|
40
|
-
- `bundled` for the shipped default path, which now prefers
|
|
39
|
+
- `bundled` for the shipped default path, which now prefers MiniLM for local stability.
|
|
41
40
|
- `onnx-local` plus `embeddingProfile` when a power user wants a known model family like Nomic with local assets.
|
|
42
41
|
- treat remote/Ollama providers as future separate backend types, not as overloads of `custom-local`.
|
package/docs/installation.md
CHANGED
|
@@ -264,7 +264,7 @@ Expected output shape:
|
|
|
264
264
|
│ Memories stored │ 0 │
|
|
265
265
|
│ Gate threshold │ 0.35 │
|
|
266
266
|
│ Abstractive model │ ready | not provisioned │
|
|
267
|
-
│ Embedding profile │
|
|
267
|
+
│ Embedding profile │ all-minilm-l6-v2 │
|
|
268
268
|
│ Message │ ok │
|
|
269
269
|
└────────────────────┴──────────────────────────────┘
|
|
270
270
|
```
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "libravdb-memory",
|
|
3
3
|
"name": "LibraVDB Memory",
|
|
4
4
|
"description": "Persistent vector memory with three-tier hybrid scoring",
|
|
5
|
-
"version": "1.4.
|
|
5
|
+
"version": "1.4.4",
|
|
6
6
|
"kind": ["memory", "context-engine"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"embeddingBackend": { "type": "string", "enum": ["bundled", "onnx-local", "custom-local"] },
|
|
16
16
|
"embeddingProfile": {
|
|
17
17
|
"type": "string",
|
|
18
|
-
"default": "
|
|
18
|
+
"default": "all-minilm-l6-v2"
|
|
19
19
|
},
|
|
20
20
|
"fallbackProfile": {
|
|
21
21
|
"type": "string",
|
|
@@ -41,6 +41,64 @@
|
|
|
41
41
|
"type": "number",
|
|
42
42
|
"default": 0.35
|
|
43
43
|
},
|
|
44
|
+
"markdownIngestionEnabled": {
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": false
|
|
47
|
+
},
|
|
48
|
+
"markdownIngestionRoots": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": { "type": "string" }
|
|
51
|
+
},
|
|
52
|
+
"markdownIngestionObsidianEnabled": {
|
|
53
|
+
"type": "boolean",
|
|
54
|
+
"default": false
|
|
55
|
+
},
|
|
56
|
+
"markdownIngestionObsidianRoots": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"items": { "type": "string" }
|
|
59
|
+
},
|
|
60
|
+
"markdownIngestionObsidianInclude": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": { "type": "string" }
|
|
63
|
+
},
|
|
64
|
+
"markdownIngestionObsidianExclude": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"items": { "type": "string" }
|
|
67
|
+
},
|
|
68
|
+
"markdownIngestionObsidianDebounceMs": {
|
|
69
|
+
"type": "number",
|
|
70
|
+
"default": 150
|
|
71
|
+
},
|
|
72
|
+
"markdownIngestionInclude": {
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": { "type": "string" }
|
|
75
|
+
},
|
|
76
|
+
"markdownIngestionExclude": {
|
|
77
|
+
"type": "array",
|
|
78
|
+
"items": { "type": "string" }
|
|
79
|
+
},
|
|
80
|
+
"markdownIngestionCollection": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"default": "global"
|
|
83
|
+
},
|
|
84
|
+
"markdownIngestionDebounceMs": {
|
|
85
|
+
"type": "number",
|
|
86
|
+
"default": 150
|
|
87
|
+
},
|
|
88
|
+
"dreamPromotionEnabled": {
|
|
89
|
+
"type": "boolean",
|
|
90
|
+
"default": false
|
|
91
|
+
},
|
|
92
|
+
"dreamPromotionDiaryPath": {
|
|
93
|
+
"type": "string"
|
|
94
|
+
},
|
|
95
|
+
"dreamPromotionUserId": {
|
|
96
|
+
"type": "string"
|
|
97
|
+
},
|
|
98
|
+
"dreamPromotionDebounceMs": {
|
|
99
|
+
"type": "number",
|
|
100
|
+
"default": 150
|
|
101
|
+
},
|
|
44
102
|
"gatingWeights": {
|
|
45
103
|
"type": "object",
|
|
46
104
|
"additionalProperties": false,
|
|
@@ -79,6 +137,11 @@
|
|
|
79
137
|
"recencyLambdaGlobal": { "type": "number" },
|
|
80
138
|
"tokenBudgetFraction": { "type": "number" },
|
|
81
139
|
"compactThreshold": { "type": "number" },
|
|
140
|
+
"compactSessionTokenBudget": {
|
|
141
|
+
"type": "number",
|
|
142
|
+
"default": 2000,
|
|
143
|
+
"description": "Auto-trigger compaction when the session accumulates this many tokens since the last compaction. Set to 0 to disable auto-compaction."
|
|
144
|
+
},
|
|
82
145
|
"ollamaUrl": { "type": "string" },
|
|
83
146
|
"compactModel": { "type": "string" },
|
|
84
147
|
"rpcTimeoutMs": { "type": "number", "default": 30000 },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xdarkicex/openclaw-memory-libravdb",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"check": "./.ts-toolchain/node_modules/.bin/tsc --noEmit && pnpm run test:ts",
|
|
28
28
|
"test:ts": "./.ts-toolchain/node_modules/.bin/tsc -p tsconfig.tests.json && node --test .ts-build/test/unit/*.test.js",
|
|
29
|
-
"test:integration": "./.ts-toolchain/node_modules/.bin/tsc -p tsconfig.tests.json && node --test .ts-build/test/integration/checklist-validation.test.js .ts-build/test/integration/host-flow.test.js .ts-build/test/integration/sidecar-lifecycle.test.js",
|
|
29
|
+
"test:integration": "./.ts-toolchain/node_modules/.bin/tsc -p tsconfig.tests.json && node --test .ts-build/test/integration/checklist-validation.test.js .ts-build/test/integration/dream-promotion.test.js .ts-build/test/integration/host-flow.test.js .ts-build/test/integration/markdown-ingest.test.js .ts-build/test/integration/sidecar-lifecycle.test.js",
|
|
30
30
|
"benchmark:session_search_mid": "./.ts-toolchain/node_modules/.bin/tsc -p tsconfig.tests.json && OPENCLAW_PROFILE_ASSEMBLE=1 node --test --test-name-pattern=\"real sidecar mid-sized session search benchmark\" .ts-build/test/integration/host-flow.test.js",
|
|
31
31
|
"gate:assemble_optimization": "./.ts-toolchain/node_modules/.bin/tsc -p tsconfig.tests.json && OPENCLAW_PROFILE_ASSEMBLE=1 OPENCLAW_ENFORCE_ASSEMBLE_EVIDENCE_GATE=1 node --test --test-name-pattern=\"real sidecar mid-sized session search benchmark\" .ts-build/test/integration/host-flow.test.js",
|
|
32
32
|
"probe:session_recall": "./.ts-toolchain/node_modules/.bin/tsc -p tsconfig.tests.json && OPENCLAW_PROFILE_ASSEMBLE=1 node --test --test-name-pattern=\"real sidecar mid-sized session search benchmark\" .ts-build/test/integration/host-flow.test.js",
|
package/src/cli.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
2
2
|
import { stdin, stdout } from "node:process";
|
|
3
3
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
|
4
4
|
import { resolveDurableNamespace } from "./durable-namespace.js";
|
|
5
|
+
import { promoteDreamDiaryFile } from "./dream-promotion.js";
|
|
5
6
|
import type { PluginRuntime } from "./plugin-runtime.js";
|
|
6
7
|
import type { LoggerLike, PluginConfig } from "./types.js";
|
|
7
8
|
|
|
@@ -26,6 +27,7 @@ type ExportResult = {
|
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
type CliOptionBag = {
|
|
30
|
+
dreamFile?: string;
|
|
29
31
|
userId?: string;
|
|
30
32
|
sessionKey?: string;
|
|
31
33
|
sessionId?: string;
|
|
@@ -94,6 +96,17 @@ export function registerMemoryCli(
|
|
|
94
96
|
journal.option("--session-id <sessionId>", "Restrict journal entries to one session id");
|
|
95
97
|
journal.option("--limit <limit>", "Maximum journal entries to show");
|
|
96
98
|
journal.action((opts) => void runJournal(runtime, opts, logger));
|
|
99
|
+
|
|
100
|
+
const dreamPromote = ensureCommand(root, "dream-promote")
|
|
101
|
+
.description("Promote vetted dream diary entries into the dedicated dream collection");
|
|
102
|
+
if (dreamPromote.requiredOption) {
|
|
103
|
+
dreamPromote.requiredOption("--user-id <userId>", "User id whose dream collection should receive the promotion");
|
|
104
|
+
dreamPromote.requiredOption("--dream-file <path>", "Dream diary markdown file to promote from");
|
|
105
|
+
} else {
|
|
106
|
+
dreamPromote.option("--user-id <userId>", "User id whose dream collection should receive the promotion");
|
|
107
|
+
dreamPromote.option("--dream-file <path>", "Dream diary markdown file to promote from");
|
|
108
|
+
}
|
|
109
|
+
dreamPromote.action((opts) => void runDreamPromote(runtime, opts, logger));
|
|
97
110
|
},
|
|
98
111
|
{
|
|
99
112
|
descriptors: [
|
|
@@ -207,6 +220,27 @@ async function runJournal(runtime: PluginRuntime, opts: CliOptionBag | undefined
|
|
|
207
220
|
}
|
|
208
221
|
}
|
|
209
222
|
|
|
223
|
+
async function runDreamPromote(runtime: PluginRuntime, opts: CliOptionBag | undefined, logger: LoggerLike): Promise<void> {
|
|
224
|
+
const userId = opts?.userId?.trim();
|
|
225
|
+
const dreamFile = opts?.dreamFile?.trim();
|
|
226
|
+
if (!userId || !dreamFile) {
|
|
227
|
+
logger.error("LibraVDB dream-promote requires --user-id <userId> and --dream-file <path>.");
|
|
228
|
+
process.exitCode = 1;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const rpc = await runtime.getRpc();
|
|
234
|
+
const result = await promoteDreamDiaryFile(rpc, { userId, diaryPath: dreamFile });
|
|
235
|
+
console.log(
|
|
236
|
+
`Promoted ${result.promoted ?? 0} dream entr${(result.promoted ?? 0) === 1 ? "y" : "ies"}; rejected ${result.rejected ?? 0}.`,
|
|
237
|
+
);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
logger.error(`LibraVDB dream promotion failed: ${formatError(error)}`);
|
|
240
|
+
process.exitCode = 1;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
210
244
|
async function confirm(prompt: string): Promise<boolean> {
|
|
211
245
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
212
246
|
try {
|