@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.
@@ -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