@xdarkicex/openclaw-memory-libravdb 1.4.3 → 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/openclaw.plugin.json +64 -1
- 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/docs/implementation.md
DELETED
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
# Implementation Notes and Interfaces
|
|
2
|
-
|
|
3
|
-
This document explains the implemented contracts that are easy to miss when
|
|
4
|
-
reading the code piecemeal.
|
|
5
|
-
|
|
6
|
-
## Memory Kind Plus Explicit Context Engine Registration
|
|
7
|
-
|
|
8
|
-
The plugin declares `kind: ["memory", "context-engine"]` in
|
|
9
|
-
[`openclaw.plugin.json`](../openclaw.plugin.json), but still registers both a
|
|
10
|
-
context engine and a memory prompt section in [`src/index.ts`](../src/index.ts).
|
|
11
|
-
|
|
12
|
-
Why:
|
|
13
|
-
|
|
14
|
-
- the intended runtime contract is that `libravdb-memory` owns both the
|
|
15
|
-
`memory` and `contextEngine` slots together
|
|
16
|
-
- the runtime behavior still needs explicit lifecycle hooks for:
|
|
17
|
-
- `bootstrap`
|
|
18
|
-
- `ingest`
|
|
19
|
-
- `assemble`
|
|
20
|
-
- `compact`
|
|
21
|
-
- the lightweight memory prompt section remains useful as a synchronous
|
|
22
|
-
capability/header hook while durable recall stays in `assemble`
|
|
23
|
-
|
|
24
|
-
This is why the code registers both `registerContextEngine("libravdb-memory", …)`
|
|
25
|
-
and `registerMemoryPromptSection(...)` instead of relying on only one hook.
|
|
26
|
-
|
|
27
|
-
On newer OpenClaw hosts, [`src/index.ts`](../src/index.ts) also registers
|
|
28
|
-
`registerMemoryRuntime(...)` as an additive bridge for the built-in
|
|
29
|
-
`memory_search` tool. That bridge reuses the same sidecar-backed retrieval path
|
|
30
|
-
instead of introducing a second memory backend.
|
|
31
|
-
|
|
32
|
-
## Why `registerMemoryRuntime` Is Additive
|
|
33
|
-
|
|
34
|
-
Implemented in [`src/memory-runtime.ts`](../src/memory-runtime.ts).
|
|
35
|
-
|
|
36
|
-
The newer OpenClaw memory runtime seam is useful, but it does not replace the
|
|
37
|
-
spec-driven architecture in this repository.
|
|
38
|
-
|
|
39
|
-
What the runtime bridge does:
|
|
40
|
-
|
|
41
|
-
- exposes a search manager for the built-in `memory_search` tool
|
|
42
|
-
- routes search into the same libraVDB collections already used by the plugin
|
|
43
|
-
- reports sidecar status through the existing JSON-RPC `status` method
|
|
44
|
-
|
|
45
|
-
What it intentionally does not do yet:
|
|
46
|
-
|
|
47
|
-
- it does not replace context-engine ingest
|
|
48
|
-
- it does not replace context-engine compaction
|
|
49
|
-
- it does not register a host flush plan that could duplicate transcript ingest
|
|
50
|
-
|
|
51
|
-
That split is deliberate. The plugin already owns ingest and compaction through
|
|
52
|
-
the context engine and sidecar, so `registerMemoryRuntime` is safe as a search
|
|
53
|
-
bridge while `registerMemoryFlushPlan` remains deferred until it can be mapped
|
|
54
|
-
cleanly onto the existing lifecycle.
|
|
55
|
-
|
|
56
|
-
## Why `before_reset` and `session_end` Stay Advisory
|
|
57
|
-
|
|
58
|
-
Implemented in [`src/lifecycle-hooks.ts`](../src/lifecycle-hooks.ts) and
|
|
59
|
-
[`src/plugin-runtime.ts`](../src/plugin-runtime.ts).
|
|
60
|
-
|
|
61
|
-
Newer OpenClaw hosts expose `before_reset` and `session_end` plugin hooks.
|
|
62
|
-
This plugin uses them, but only as hints into the sidecar.
|
|
63
|
-
|
|
64
|
-
Current behavior:
|
|
65
|
-
|
|
66
|
-
- `before_reset` forwards session identifiers, reset reason, and observed
|
|
67
|
-
message count
|
|
68
|
-
- `session_end` forwards end reason, archive linkage, and follow-on session
|
|
69
|
-
metadata
|
|
70
|
-
- the sidecar appends the hint to an internal lifecycle journal and performs a
|
|
71
|
-
best-effort flush/ack
|
|
72
|
-
|
|
73
|
-
Important boundary:
|
|
74
|
-
|
|
75
|
-
- these hooks are not the source of truth for memory correctness
|
|
76
|
-
- failure to deliver them must not break the session
|
|
77
|
-
- ingest, retrieval, and compaction still belong to the context engine and
|
|
78
|
-
sidecar runtime we control
|
|
79
|
-
- lifecycle journal entries live in an internal collection and are only visible
|
|
80
|
-
through explicit status/debug surfaces such as `openclaw memory journal`
|
|
81
|
-
- lifecycle retention is bounded and enforced on append by pruning the oldest
|
|
82
|
-
journal entries first
|
|
83
|
-
|
|
84
|
-
## Why Ingest Is Fire-and-Forget
|
|
85
|
-
|
|
86
|
-
Implemented in [`src/context-engine.ts`](../src/context-engine.ts).
|
|
87
|
-
|
|
88
|
-
Session insertion is intentionally fire-and-forget:
|
|
89
|
-
|
|
90
|
-
- the active conversation should not block on persistence
|
|
91
|
-
- session memory is useful immediately, but not allowed to become a hard
|
|
92
|
-
dependency for response generation
|
|
93
|
-
|
|
94
|
-
The current code writes to `session:<sessionId>` asynchronously and only then
|
|
95
|
-
attempts the more expensive durable-promotion path for user turns.
|
|
96
|
-
|
|
97
|
-
## Why Gating Uses Exactly Two Searches
|
|
98
|
-
|
|
99
|
-
Implemented in [`sidecar/server/rpc.go`](../sidecar/server/rpc.go).
|
|
100
|
-
|
|
101
|
-
`gating_scalar` performs exactly:
|
|
102
|
-
|
|
103
|
-
1. one search against `turns:<userId>`
|
|
104
|
-
2. one search against `user:<userId>`
|
|
105
|
-
|
|
106
|
-
Novelty and durable-memory saturation reuse the same `user:` hit set. This keeps
|
|
107
|
-
the RPC bounded and predictable. There is no third store query for novelty.
|
|
108
|
-
|
|
109
|
-
## Why the Token Estimator Uses Bytes/4 in the Gate
|
|
110
|
-
|
|
111
|
-
Implemented in [`sidecar/compact/tokens.go`](../sidecar/compact/tokens.go).
|
|
112
|
-
|
|
113
|
-
The gate's specificity term uses:
|
|
114
|
-
|
|
115
|
-
$$
|
|
116
|
-
\mathrm{EstimateTokens}(t)=\max(\lfloor \mathrm{len}(t)/4 \rfloor, 1)
|
|
117
|
-
$$
|
|
118
|
-
|
|
119
|
-
Why not word count:
|
|
120
|
-
|
|
121
|
-
- word count behaves badly on code
|
|
122
|
-
- file paths, stack traces, and identifiers are token-dense but word-sparse
|
|
123
|
-
- bytes/4 is cheap and stable across prose, code, and mixed technical content
|
|
124
|
-
|
|
125
|
-
Important boundary:
|
|
126
|
-
|
|
127
|
-
- this is the gating estimator
|
|
128
|
-
- prompt-budget fitting uses a separate host-side chars-per-token heuristic in
|
|
129
|
-
[`src/tokens.ts`](../src/tokens.ts)
|
|
130
|
-
|
|
131
|
-
## Why the Daemon Uses a Stable Endpoint
|
|
132
|
-
|
|
133
|
-
Implemented in [`sidecar/main.go`](../sidecar/main.go) and
|
|
134
|
-
[`src/sidecar.ts`](../src/sidecar.ts).
|
|
135
|
-
|
|
136
|
-
The daemon binds to a stable, predictable local endpoint instead of advertising
|
|
137
|
-
a per-process endpoint on stdout.
|
|
138
|
-
|
|
139
|
-
Why:
|
|
140
|
-
|
|
141
|
-
- the published plugin no longer spawns the process itself
|
|
142
|
-
- connect-only plugin startup needs a known endpoint contract
|
|
143
|
-
- user services such as `systemd --user`, launchd, and Homebrew service support
|
|
144
|
-
work better with a stable socket or loopback address
|
|
145
|
-
- Windows still uses a fixed loopback TCP endpoint because Unix sockets are not
|
|
146
|
-
the common user-service path there
|
|
147
|
-
|
|
148
|
-
Current defaults:
|
|
149
|
-
|
|
150
|
-
- macOS/Linux: `unix:$HOME/.clawdb/run/libravdb.sock`
|
|
151
|
-
- Windows: `tcp:127.0.0.1:37421`
|
|
152
|
-
|
|
153
|
-
The plugin resolves that configured endpoint and then establishes the JSON-RPC
|
|
154
|
-
transport.
|
|
155
|
-
|
|
156
|
-
## Why Degraded Mode Continues the Session
|
|
157
|
-
|
|
158
|
-
Implemented in [`src/sidecar.ts`](../src/sidecar.ts) and
|
|
159
|
-
[`src/context-engine.ts`](../src/context-engine.ts).
|
|
160
|
-
|
|
161
|
-
If the daemon connection fails repeatedly, the plugin enters degraded mode instead of
|
|
162
|
-
failing the chat session.
|
|
163
|
-
|
|
164
|
-
Why:
|
|
165
|
-
|
|
166
|
-
- memory augmentation is valuable, but it is not allowed to become a hard
|
|
167
|
-
dependency for the core conversation path
|
|
168
|
-
- the safe fallback is "continue without memory augmentation" rather than
|
|
169
|
-
"reject the entire turn"
|
|
170
|
-
|
|
171
|
-
This is deliberate fault containment.
|
|
172
|
-
|
|
173
|
-
## `ownsCompaction: true`
|
|
174
|
-
|
|
175
|
-
Implemented in [`src/context-engine.ts`](../src/context-engine.ts).
|
|
176
|
-
|
|
177
|
-
The context engine factory returns `ownsCompaction: true`.
|
|
178
|
-
|
|
179
|
-
This tells the host that compaction belongs to the memory engine lifecycle
|
|
180
|
-
itself. In this plugin, compaction is not an optional helper or an external
|
|
181
|
-
maintenance job; it is part of the actual memory system contract.
|
|
182
|
-
|
|
183
|
-
## Interface: `GatingConfig`
|
|
184
|
-
|
|
185
|
-
Defined in [`sidecar/compact/gate.go`](../sidecar/compact/gate.go).
|
|
186
|
-
|
|
187
|
-
```go
|
|
188
|
-
type GatingConfig struct {
|
|
189
|
-
W1c float64
|
|
190
|
-
W2c float64
|
|
191
|
-
W3c float64
|
|
192
|
-
W1t float64
|
|
193
|
-
W2t float64
|
|
194
|
-
W3t float64
|
|
195
|
-
TechNorm float64
|
|
196
|
-
Threshold float64
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Field meanings:
|
|
201
|
-
|
|
202
|
-
- `W1c`, `W2c`, `W3c`: conversational-branch weights for novelty `H`,
|
|
203
|
-
repetition gate `R`, and conversational structure `D`
|
|
204
|
-
- `W1t`, `W2t`, `W3t`: technical-branch weights for specificity `P`,
|
|
205
|
-
actionability `A`, and technical structure `Dtech`
|
|
206
|
-
- `TechNorm`: normalization constant for technical-density saturation
|
|
207
|
-
- `Threshold`: durable-promotion cutoff used by the host
|
|
208
|
-
|
|
209
|
-
Contract:
|
|
210
|
-
|
|
211
|
-
- all weights are intended to be in `[0,1]`
|
|
212
|
-
- each branch should sum to `1.0` by convention
|
|
213
|
-
- `TechNorm <= 0` is normalized back to the default inside `computeT`
|
|
214
|
-
- zero values are not generally meaningful outside tests; callers should use
|
|
215
|
-
`DefaultGatingConfig()` or config-derived values from [`sidecar/main.go`](../sidecar/main.go)
|
|
216
|
-
|
|
217
|
-
## Interface: `GatingSignals`
|
|
218
|
-
|
|
219
|
-
Defined in [`sidecar/compact/gate.go`](../sidecar/compact/gate.go).
|
|
220
|
-
|
|
221
|
-
```go
|
|
222
|
-
type GatingSignals struct {
|
|
223
|
-
G float64
|
|
224
|
-
T float64
|
|
225
|
-
H float64
|
|
226
|
-
R float64
|
|
227
|
-
D float64
|
|
228
|
-
InputFreq float64
|
|
229
|
-
MemSaturation float64
|
|
230
|
-
P float64
|
|
231
|
-
A float64
|
|
232
|
-
Dtech float64
|
|
233
|
-
Gconv float64
|
|
234
|
-
Gtech float64
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
Field meanings:
|
|
239
|
-
|
|
240
|
-
- `G`: final gate score in `[0,1]`
|
|
241
|
-
- `T`: technical-density branch weight in `[0,1]`
|
|
242
|
-
- `H`: novelty score in `[0,1]`
|
|
243
|
-
- `R`: repetition product gate in `[0,1]`
|
|
244
|
-
- `D`: conversational structural load in `[0,1]`
|
|
245
|
-
- `InputFreq`: normalized repeated-mention signal in `[0,1]`
|
|
246
|
-
- `MemSaturation`: normalized durable-memory saturation signal in `[0,1]`
|
|
247
|
-
- `P`: technical specificity score in `[0,1]`
|
|
248
|
-
- `A`: technical actionability score in `[0,1]`
|
|
249
|
-
- `Dtech`: technical structural load in `[0,1]`
|
|
250
|
-
- `Gconv`: weighted conversational branch score
|
|
251
|
-
- `Gtech`: weighted technical branch score
|
|
252
|
-
|
|
253
|
-
Zero-value behavior:
|
|
254
|
-
|
|
255
|
-
- an all-zero `GatingSignals` struct does not mean "valid low-confidence turn";
|
|
256
|
-
it usually means "not computed yet"
|
|
257
|
-
- missing metadata readers should treat absent values as `0.0` and not panic
|
|
258
|
-
|
|
259
|
-
Inputs vs outputs:
|
|
260
|
-
|
|
261
|
-
- `GatingConfig` is input
|
|
262
|
-
- `turnHits`, `memHits`, and `text` are inputs to `ComputeGating`
|
|
263
|
-
- `GatingSignals` is output and is intended to be persisted in metadata
|
|
264
|
-
|
|
265
|
-
## Interface: JSON-RPC Surface
|
|
266
|
-
|
|
267
|
-
Implemented in [`sidecar/server/rpc.go`](../sidecar/server/rpc.go).
|
|
268
|
-
|
|
269
|
-
Method names are snake_case in the actual protocol.
|
|
270
|
-
|
|
271
|
-
### `health`
|
|
272
|
-
|
|
273
|
-
- request: `{}`
|
|
274
|
-
- response: `{ ok: boolean, message: string }`
|
|
275
|
-
- errors: none expected unless transport fails
|
|
276
|
-
|
|
277
|
-
### `status`
|
|
278
|
-
|
|
279
|
-
- request: `{}`
|
|
280
|
-
- response:
|
|
281
|
-
`{ ok, message, turnCount, memoryCount, gatingThreshold, abstractiveReady, embeddingProfile }`
|
|
282
|
-
- errors: none expected unless transport fails
|
|
283
|
-
|
|
284
|
-
### `ensure_collections`
|
|
285
|
-
|
|
286
|
-
- request: `{ collections: string[] }`
|
|
287
|
-
- response: `{ ok: true }`
|
|
288
|
-
- errors:
|
|
289
|
-
- collection creation failure in the Go store
|
|
290
|
-
|
|
291
|
-
### `insert_text`
|
|
292
|
-
|
|
293
|
-
- request:
|
|
294
|
-
`{ collection: string, id: string, text: string, metadata: object }`
|
|
295
|
-
- response: `{ ok: true }`
|
|
296
|
-
- errors:
|
|
297
|
-
- embedding failure
|
|
298
|
-
- record validation failure
|
|
299
|
-
- store insertion failure
|
|
300
|
-
|
|
301
|
-
### `gating_scalar`
|
|
302
|
-
|
|
303
|
-
- request: `{ userId: string, text: string }`
|
|
304
|
-
- response: full `GatingSignals` JSON payload
|
|
305
|
-
- errors:
|
|
306
|
-
- search failure on `turns:<userId>` or `user:<userId>`
|
|
307
|
-
|
|
308
|
-
### `search_text`
|
|
309
|
-
|
|
310
|
-
- request:
|
|
311
|
-
`{ collection: string, text: string, k: number, excludeIds?: string[] }`
|
|
312
|
-
- response: `{ results: SearchResult[] }`
|
|
313
|
-
- errors:
|
|
314
|
-
- query embedding failure
|
|
315
|
-
- store search failure
|
|
316
|
-
|
|
317
|
-
### `list_by_meta`
|
|
318
|
-
|
|
319
|
-
- request: `{ collection: string, key: string, value: string }`
|
|
320
|
-
- response: `{ results: SearchResult[] }`
|
|
321
|
-
- errors:
|
|
322
|
-
- store listing failure
|
|
323
|
-
|
|
324
|
-
### `export_memory`
|
|
325
|
-
|
|
326
|
-
- request: `{ userId?: string }`
|
|
327
|
-
- response:
|
|
328
|
-
`{ records: Array<{ collection, id, text, metadata }> }`
|
|
329
|
-
- errors:
|
|
330
|
-
- collection listing failure
|
|
331
|
-
|
|
332
|
-
### `flush_namespace`
|
|
333
|
-
|
|
334
|
-
- request: `{ userId: string }`
|
|
335
|
-
- response: `{ ok: true }`
|
|
336
|
-
- errors:
|
|
337
|
-
- missing `userId`
|
|
338
|
-
- delete-by-prefix failure
|
|
339
|
-
|
|
340
|
-
### `delete`
|
|
341
|
-
|
|
342
|
-
- request: `{ collection: string, id: string }`
|
|
343
|
-
- response: `{ ok: true }`
|
|
344
|
-
- errors:
|
|
345
|
-
- delete failure
|
|
346
|
-
|
|
347
|
-
### `delete_batch`
|
|
348
|
-
|
|
349
|
-
- request: `{ collection: string, ids: string[] }`
|
|
350
|
-
- response: `{ ok: true }`
|
|
351
|
-
- errors:
|
|
352
|
-
- batch delete failure
|
|
353
|
-
|
|
354
|
-
### `compact_session`
|
|
355
|
-
|
|
356
|
-
- request: `{ sessionId: string, force: boolean, targetSize?: number }`
|
|
357
|
-
- response:
|
|
358
|
-
`{ didCompact, clustersFormed, turnsRemoved, summaryMethod, meanConfidence }`
|
|
359
|
-
- errors:
|
|
360
|
-
- missing session id
|
|
361
|
-
- extractive summarizer unavailable
|
|
362
|
-
- summary insertion failure
|
|
363
|
-
- summarization failure
|
|
364
|
-
|
|
365
|
-
### `flush`
|
|
366
|
-
|
|
367
|
-
- request: `{}`
|
|
368
|
-
- response: `{ ok: true }`
|
|
369
|
-
- errors:
|
|
370
|
-
- store flush failure
|
|
371
|
-
|
|
372
|
-
## Interface: Context Engine Lifecycle
|
|
373
|
-
|
|
374
|
-
Implemented in [`src/context-engine.ts`](../src/context-engine.ts).
|
|
375
|
-
|
|
376
|
-
The factory returns an object with this effective shape:
|
|
377
|
-
|
|
378
|
-
```ts
|
|
379
|
-
{
|
|
380
|
-
ownsCompaction: true,
|
|
381
|
-
bootstrap(args: ContextBootstrapArgs): Promise<{ ok: true }>,
|
|
382
|
-
ingest(args: ContextIngestArgs): Promise<{ ingested: boolean }>,
|
|
383
|
-
assemble(args: ContextAssembleArgs): Promise<{
|
|
384
|
-
messages: MemoryMessage[],
|
|
385
|
-
estimatedTokens: number,
|
|
386
|
-
systemPromptAddition: string,
|
|
387
|
-
}>,
|
|
388
|
-
compact(args: ContextCompactArgs): Promise<{ ok: true, compacted: boolean }>,
|
|
389
|
-
}
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
### `bootstrap`
|
|
393
|
-
|
|
394
|
-
Input:
|
|
395
|
-
|
|
396
|
-
- `sessionId`
|
|
397
|
-
- `userId`
|
|
398
|
-
|
|
399
|
-
Behavior:
|
|
400
|
-
|
|
401
|
-
- ensures `session:`, `turns:`, `user:`, and `global` collections exist
|
|
402
|
-
|
|
403
|
-
### `ingest`
|
|
404
|
-
|
|
405
|
-
Input:
|
|
406
|
-
|
|
407
|
-
- `sessionId`
|
|
408
|
-
- `userId`
|
|
409
|
-
- `message`
|
|
410
|
-
- `isHeartbeat?`
|
|
411
|
-
|
|
412
|
-
Behavior:
|
|
413
|
-
|
|
414
|
-
- must not block the session on best-effort persistence
|
|
415
|
-
- writes all non-heartbeat messages to session memory
|
|
416
|
-
- only user turns go through the durable gating path
|
|
417
|
-
|
|
418
|
-
### `assemble`
|
|
419
|
-
|
|
420
|
-
Input:
|
|
421
|
-
|
|
422
|
-
- `sessionId`
|
|
423
|
-
- `userId`
|
|
424
|
-
- `messages`
|
|
425
|
-
- `tokenBudget`
|
|
426
|
-
|
|
427
|
-
Behavior:
|
|
428
|
-
|
|
429
|
-
- must not mutate the incoming `messages` array in place
|
|
430
|
-
- searches three scopes in parallel
|
|
431
|
-
- hybrid-ranks and budget-fits the result set
|
|
432
|
-
- prepends selected memories as synthetic system messages
|
|
433
|
-
- falls back cleanly to the original message list on failure
|
|
434
|
-
|
|
435
|
-
### `compact`
|
|
436
|
-
|
|
437
|
-
Input:
|
|
438
|
-
|
|
439
|
-
- `sessionId`
|
|
440
|
-
- `force?`
|
|
441
|
-
- `targetSize?`
|
|
442
|
-
|
|
443
|
-
Behavior:
|
|
444
|
-
|
|
445
|
-
- delegates to the sidecar `compact_session` RPC
|
|
446
|
-
- returns `{ ok: true, compacted }`
|
|
447
|
-
- treats compaction failure as non-fatal to the active session
|