@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 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 lives in:
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/implementation.md](./docs/implementation.md)
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
- Versioned `*-v*` design docs are the reviewed authoritative references when a
4
- legacy non-versioned predecessor also exists. Older non-versioned docs are kept
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) - End-to-end component model, turn lifecycle, compaction flow, and degraded behavior.
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.
@@ -1,182 +1,97 @@
1
1
  # System Architecture
2
2
 
3
- This document describes the current implemented architecture, not just the
4
- design intent. Every component and data flow here maps to code in the
5
- repository as of the current `main` branch.
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
- ## 1. Component Map
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 process\n(TypeScript plugin shell)"]
12
- CE["Context engine factory\nbootstrap / ingest / assemble / compact"]
13
- MPS["memoryPromptSection\nstatic header"]
14
- Runtime["Plugin runtime\nlazy daemon connect + RPC client"]
15
- Sidecar["Go daemon process"]
16
- RPC["JSON-RPC over newline-delimited frames\nUnix socket or TCP loopback on Windows"]
17
- Store["LibraVDB store on disk"]
18
- Session["session:<sessionId>"]
19
- Turns["turns:<userId>"]
20
- User["user:<userId>"]
21
- Global["global"]
22
- Dirty["_tier_dirty"]
23
- Embed["ONNX embedding engine"]
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 --> Extractive
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
- Implementation anchors:
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
- Current implementation details that matter:
43
+ ### `memoryPromptSection`
103
44
 
104
- - user/global hits are cached within `assemble` and reused on repeated queries
105
- - `assemble` falls back to the unmodified message list on RPC failure
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
- ## 3. Compaction Data Flow
48
+ ### `ingest`
110
49
 
111
- Implemented primarily in [`src/context-engine.ts`](../src/context-engine.ts)
112
- and [`sidecar/compact/summarize.go`](../sidecar/compact/summarize.go).
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
- When compaction is triggered:
53
+ ### `assemble`
115
54
 
116
- 1. the host calls `compact_session` with `{ sessionId, force, targetSize }`
117
- 2. the daemon loads eligible non-summary turns from `session:<sessionId>`
118
- 3. turns are sorted by `(ts, id)` and partitioned into deterministic
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
- Current implementation facts:
59
+ ### `compact`
128
60
 
129
- - compaction only touches `session:<sessionId>`
130
- - raw source turns are preserved if summary insertion fails
131
- - delete failure logs and leaves the inserted summary in place
132
- - compaction logs `cluster_id`, `mean_gating_score`, and `summarizer_used`
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
- ## 4. Failure Modes and Degraded Behavior
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
- The table below reflects current code behavior, with notes where it diverges
137
- from the original spec phrasing.
69
+ ## Current Boundaries
138
70
 
139
- | Failure | Current behavior | User impact |
140
- |---|---|---|
141
- | Daemon unavailable on first RPC use | `getRpc()` rejects when first connect or health check fails | That hook fails or falls back, but plugin registration itself does not crash eagerly |
142
- | Daemon connection closes mid-session | `SidecarSupervisor` retries with exponential backoff until retry budget is exhausted, then enters degraded mode | Memory becomes unavailable until the daemon is reachable again |
143
- | `memoryPromptSection` failure | returns a static header with no RPC dependency | Prompt section stays available and does not block the run |
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
- Relevant code:
77
+ ## Failure Handling
151
78
 
152
- - retry/degraded behavior: [`src/sidecar.ts`](../src/sidecar.ts)
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
- ## 5. Gating Decision Path
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
- The gating decision spans both layers:
85
+ That keeps the chat usable even when the memory backend is temporarily down.
160
86
 
161
- 1. `ingest` writes the user turn to `turns:<userId>`
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
- If the gate fails:
89
+ This architecture keeps the host integration simple while still supporting:
174
90
 
175
- - the turn still exists in `session:<sessionId>`
176
- - the turn still exists in `turns:<userId>`
177
- - the turn is not promoted into `user:<userId>`
178
- - downstream durable recall and compaction routing cannot use that turn's
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
- That makes the gate a durable-memory admission control, not a full-ingestion
182
- blocker.
96
+ In short, the plugin owns the lifecycle contract, and the sidecar owns the
97
+ heavy lifting.
@@ -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 [mathematics-v2.md](./mathematics-v2.md)
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: `nomic-embed-text-v1.5`
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 and Nomic are equivalent on the current lexical and paraphrase baseline.
13
- - Nomic materially outperforms MiniLM on cross-domain ranking quality.
14
- - Nomic is the only profile that clears the long-context baseline once sliding-window document embedding is applied.
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 Nomic and falls back to MiniLM if the primary profile is unavailable.
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`.
@@ -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 │ nomic-embed-text-v1.5
267
+ │ Embedding profile │ all-minilm-l6-v2
268
268
  │ Message │ ok │
269
269
  └────────────────────┴──────────────────────────────┘
270
270
  ```
@@ -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.2",
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": "nomic-embed-text-v1.5"
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.2",
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 {