@unblocklabs/unblock-memory 0.3.22 → 0.3.24

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.
@@ -0,0 +1,607 @@
1
+ # Retrieval, whisperers and maintenance
2
+
3
+ [Overview](../README.md) · [Configuration and credentials](configuration.md)
4
+
5
+ ## Search and read
6
+
7
+ `memory_search` searches every configured **non-skill** corpus for this agent by
8
+ default. Select named corpora, or use `["all"]` alone. An empty list or unknown
9
+ name is an error. Default: **5 results**, vector `minScore: 0.3`; configurable
10
+ `maxResults` is 1–20 and `minScore` is 0–1. No TypeSafe, BM25, query expansion
11
+ or reranker runs in this tool.
12
+
13
+ Example tool input (the `memory` corpus exists by default):
14
+
15
+ ```json
16
+ { "query": "Who approved the staging rollout?", "corpora": ["memory"], "maxResults": 5 }
17
+ ```
18
+
19
+ Results use compact JSON and carry `path`, `startLine`, `endLine`, `snippet`,
20
+ `corpus`, and `score`/`vectorScore` rounded to hundredths. Ranking and threshold
21
+ filtering still use full precision. The constant `source` and top-level `provider`
22
+ fields are omitted; `path` plus line numbers replace the redundant `citation`.
23
+ Session hits also carry session metadata and, when available,
24
+ `messageTimestamp`: the original timestamp text (including timezone) of the message
25
+ containing the matched chunk. It stays tied to that message even when the excerpt
26
+ expands to the surrounding turn. Missing timestamps are omitted, not replaced by
27
+ session start time. Vector similarity is a retrieval signal, not confidence in
28
+ the truth of a claim.
29
+
30
+ Session `snippet` values are arrays of messages, in source order:
31
+
32
+ ```json
33
+ [{ "type": "assistant", "name": "Bill", "timestamp": "2026-08-17 15:40:09 EDT", "body": "**Original message text**, including Markdown." }]
34
+ ```
35
+
36
+ Each message has its own timestamp. Only generated transcript headings are removed;
37
+ body formatting, code, mentions and HTML entities are preserved. `partial: true`
38
+ means the returned body is an excerpt, not the complete message. Metadata is resolved
39
+ from the full indexed document even when a chunk starts mid-message. Assistant agent
40
+ IDs are mapped to the configured identity name when available; other names are kept.
41
+ Unattributable legacy text is retained as `{ "body": "…", "partial": true }`, without
42
+ inventing a role, name or timestamp. File-backed snippets remain strings.
43
+ `memory_get` still returns indexed Markdown, and internal search/Whisperer contracts
44
+ still use strings. This changes output structure, not retrieval ranking.
45
+
46
+ Read the **returned** path, substituting its actual source/line values:
47
+
48
+ ```json
49
+ { "path": "qmd://source-RETURNED_ID/memory/example.md", "from": 12, "lines": 40 }
50
+ ```
51
+
52
+ `memory_get` reads the indexed snapshot, not arbitrary filesystem paths or QMD
53
+ docids. It accepts exact configured `qmd://` paths, excludes skills, defaults to
54
+ 120 lines and bounds content to 12,000 characters. `from` is 1-based; requested
55
+ `lines` is 1–1,000. `status: "ok"` can still have `truncated: true`: use
56
+ `nextFrom` to continue when present. A single oversized line can be clipped
57
+ without a continuation line; inspect the authorized original source if that
58
+ complete line matters. `not_found` and `unavailable` are not successful empty reads.
59
+
60
+ Search snippets are leads. Inspect attribution, qualifications, dates and adjacent
61
+ context before relying on a factual claim. An old plan is not proof it happened,
62
+ and memory does not grant permission to act. No results may mean the wrong corpus,
63
+ a high threshold, an unsynced session or an unavailable index—not absence of the fact.
64
+ Search initialization errors include an `error` alongside empty `results`;
65
+ other failures may surface as tool errors. Investigate them instead of reporting
66
+ “nothing is remembered.”
67
+
68
+ Corpora and session filters select evidence; they are not audience ACLs. Normal
69
+ tools can access this agent's configured non-skill corpora, not just its current
70
+ chat. Only index material suitable for the agent's tool callers. Per-feature
71
+ TypeSafe allowlists do not restrict ordinary retrieval.
72
+
73
+ ## QMD search modes
74
+
75
+ The following describes QMD's TypeSafe-ranked `query` API (2.10+). Use the exact
76
+ dependency in the installed plugin's package metadata when diagnosing an older
77
+ installation.
78
+
79
+ | Surface | Retrieval/ranking | Score / useful distinction |
80
+ | --- | --- | --- |
81
+ | Plugin `memory_search` | Direct vector search, expansion off | Vector similarity; ordinary agent recall |
82
+ | QMD CLI `search` / SDK `searchLex` | Local BM25 | Normalized lexical relevance; useful for names, identifiers and exact phrases |
83
+ | QMD CLI/SDK `vsearch` | Local vectors; standalone default includes local query expansion | Vector similarity; `--no-expand` / `expand:false` selects literal retrieval |
84
+ | QMD CLI/MCP `query` / SDK `search` | Literal vectors + BM25, deduplicated source excerpts, independent TypeSafe usefulness ranking | Usefulness divided by 3, not cosine similarity; default limit 10/minScore 0 |
85
+ | QMD `query --no-rerank` / SDK/MCP `rerank:false` | Local retrieval, best reciprocal retrieval-rank ordering | 1 / rank; explicitly skips remote scoring |
86
+
87
+ Plain `query` takes `ceil(1.5 × limit)` candidates per backend, without local
88
+ expansion or local reranking. Explicit `lex`/`vec`/`hyde` variants share the
89
+ TypeSafe ranking policy by default. A supplied hypothetical `hyde` passage is
90
+ a retrieval input, not evidence. Scores across modes are not interchangeable.
91
+
92
+ **Migration:** Memory 0.3.22 removed the temporary `memory_xsearch` tool. QMD's
93
+ `query` provides that hybrid retrieval/ranking functionality; there is no plugin
94
+ `memory_query`, and `memory_search` did not become hybrid.
95
+
96
+ Standalone QMD is a separate entry point. Its project/named/global index is not
97
+ automatically this agent's `unblock-memory/index.sqlite`. Plugin corpus names
98
+ also are not QMD collection IDs: each configured path maps to a `source-<hash>`
99
+ collection, and a corpus can contain several. Establish the intended index,
100
+ collection scope and installed QMD version before using CLI/MCP as an alternative.
101
+ Do not run standalone collection/update/embed maintenance against a live
102
+ plugin-managed index as a casual search fallback.
103
+
104
+ QMD CLI/MCP read `TYPESAFE_API_KEY` or `TYPESAFE_API_KEY_FILE` from their own
105
+ process; SDK callers can supply credentials. Plugin `typesafe.apiKeyFile` does
106
+ not export a key to those processes. Ranked query sends the query, intent,
107
+ selected excerpts, source paths and evaluation time to TypeSafe. Missing keys or
108
+ scoring failures return errors, not vector fallback or a successful empty answer.
109
+ Use an explicitly local mode when appropriate.
110
+
111
+ See the [QMD guide](https://github.com/unblocklabs-ai/qmd#readme) for CLI/MCP syntax.
112
+ Private skill frontmatter retrieval and response-audit lexical investigations are
113
+ internal workflows, not extra public plugin search modes.
114
+
115
+ ## Sessions
116
+
117
+ Add a `sessions` entry alongside `memory` in your configured corpora (see the
118
+ [session profile](configuration.md#example-profiles)). Start a manual refresh with
119
+ `memory_sync_sessions({})`, then poll `memory_sync_status({})` until completed
120
+ or failed. `started` / `already_running` only acknowledge background work.
121
+ Check completion counts, including per-session failures, before assuming freshness.
122
+
123
+ Use `sessionFilter` to restrict session results by metadata while leaving file
124
+ corpora searchable. Supported fields are `startedFrom` and `startedTo`
125
+ (inclusive ISO 8601 timestamps), `provider`, `chatType`, `accountId`, and
126
+ `conversationId`:
127
+
128
+ ```json
129
+ {
130
+ "query": "deployment decision",
131
+ "sessionFilter": {
132
+ "startedFrom": "2026-08-01T00:00:00Z",
133
+ "provider": "slack",
134
+ "chatType": "channel"
135
+ }
136
+ }
137
+ ```
138
+
139
+ Provider matching is case-normalized; `chatType` uses the lowercase values
140
+ shown in the configuration example. Account and conversation IDs are trimmed
141
+ and matched exactly. When only `sessions` is selected and no sessions match,
142
+ search returns no results. With other corpora selected, their results remain
143
+ eligible.
144
+
145
+ The date bounds remain inclusive **session start times**; `messageTimestamp`
146
+ dates the matched message but is not a search filter. These metadata filters
147
+ do not change the selected file corpora or authorize disclosure to another audience.
148
+
149
+ The optional `sessions` corpus reads the current agent's normal OpenClaw SQLite
150
+ store and indexes its active user/assistant transcript branch. It defaults to
151
+ channel and group conversations; add `direct` explicitly to include DMs. A
152
+ session vector hit expands to its complete user/assistant turn when the turn
153
+ fits `maxExpandedTokens`, or to its complete enclosing message when only that
154
+ fits. The default is `500`; the original semantic chunk is preserved when
155
+ neither complete context fits, so expansion never clips the matched evidence.
156
+ Run
157
+ `memory_sync_sessions` to start a refresh, then use `memory_sync_status` to
158
+ check its progress or result. The read-only adapter explicitly supports OpenClaw
159
+ agent database schemas 17, 18, and 19 and validates its required columns before
160
+ reading. Projections are private derived Markdown under the
161
+ agent's `unblock-memory/sessions` state directory and can be rebuilt from
162
+ OpenClaw at any time. Their embedded text contains only `# Transcript` and
163
+ role-labeled, timestamped speaker messages; filtering metadata remains in the
164
+ session manifest. Projection v7 also retains message metadata and exact character
165
+ boundaries there, without duplicating message bodies. Readers use those boundaries
166
+ only when the projection hash matches the indexed document. Older/mismatched snapshots
167
+ use a conservative heading parser that skips code fences and blockquotes; an unfenced
168
+ literal heading can still be ambiguous until the next session refresh rebuilds the
169
+ metadata. The projected file modification time matches the session
170
+ start time for meaningful chronological cluster reads. Session results include
171
+ provider, chat type, conversation identity, and start time as an ISO 8601 timestamp. They
172
+ participate in the same search and clustering index as file memory. The plugin
173
+ automatically checks each configured agent's sessions every 60 minutes while
174
+ the Gateway runs. Set `syncIntervalMinutes` on the `sessions` corpus to an integer
175
+ from `1` to `1440`, or `0` for manual-only syncing. For example:
176
+
177
+ ```json
178
+ { "name": "sessions", "kind": "sessions", "syncIntervalMinutes": 60 }
179
+ ```
180
+
181
+ The first refresh runs after one interval, not during startup. Restart the
182
+ Gateway after changing the interval. Refreshes are incremental; an already-running
183
+ sync is skipped, and failures are visible through `memory_sync_status` and retried
184
+ at the next interval. `memory_sync_sessions` still provides an immediate manual
185
+ refresh. Syncing and embedding run inside the Gateway process, without an LLM turn.
186
+
187
+ Quiet checks compare source metadata and the last successful index checkpoint
188
+ before initializing the memory manager. Unchanged sessions skip QMD updates and
189
+ embedding. New assistant answers count too, not just human messages. Changed
190
+ transcripts are projected and content-hashed; tool-only or filtered additions
191
+ that leave the indexed text unchanged also skip indexing. Empty/filtered sessions
192
+ are remembered. Index changes, missing projections, changed projection settings,
193
+ QMD upgrades and incomplete runs invalidate the skip checkpoint; `force: true`
194
+ bypasses both gates. `memory_sync_status` reports `lastCheckedAt`, `lastIndexedAt`
195
+ and `skipReason` (`no_changes` or `no_indexable_changes`) separately. Existing
196
+ explicit intervals remain unchanged on upgrade; set them to `60` for hourly checks.
197
+
198
+ Indexes live at `~/.openclaw/agents/<agentId>/unblock-memory/index.sqlite` (or the
199
+ equivalent configured OpenClaw state directory). Durable agent-supplied event
200
+ dates, maintenance proposals, people/dossiers and response audits live separately
201
+ in `unblock-memory.sqlite`, so a QMD
202
+ index rebuild does not discard them. The first lookup builds the index;
203
+ Markdown filesystem changes queue a debounced, serialized background refresh.
204
+
205
+ ### Loggie meeting interoperability
206
+
207
+ Loggie v0.1.12+ persists versioned, speaker-attributed meeting Markdown separately
208
+ from its workflow prompt. Session projection recognizes that format and also
209
+ normalizes complete legacy Loggie JSON envelopes. Unrecognized, malformed or
210
+ truncated legacy payloads keep their original text; source sessions are never
211
+ rewritten. Summaries remain labeled as generated material, distinct from speech.
212
+
213
+ QMD groups adjacent speaker blocks rather than forcing one chunk per speaker.
214
+ Search expands around the matching exchange within its existing budget. Long
215
+ monologue excerpts regain the source speaker label while citations still point
216
+ to the exact original source lines. No identity or timestamp is invented.
217
+
218
+ Within a session, identical replayed transcripts are suppressed; distinct
219
+ complete revisions with ordered source sequence numbers retain their history
220
+ and assistant follow-ups, with older versions marked superseded. Account,
221
+ workspace, meeting and external transcript identifiers scope the comparison.
222
+ Ambiguous/partial revisions are preserved. Separate session windows are not
223
+ globally deduplicated.
224
+
225
+ Use the session projection as the searchable meeting copy. Loggie raw archives
226
+ remain opt-in and should stay outside file-corpus globs (new default:
227
+ `transcripts/loggie-archive`). Memory never follows archive paths embedded in
228
+ messages. Truncated sessions stay explicitly incomplete; enabling archive
229
+ enrichment is not part of this version.
230
+
231
+ ### Conservative ingestion cleanup
232
+
233
+ Session projections unwrap complete, recognized task/attachment envelopes while
234
+ keeping the actual result, task/status, filename, MIME type, and untrusted-content
235
+ label. Internal task cleanup requires structured inter-session provenance, not
236
+ just matching text. Unknown formats, malformed envelopes, and code examples stay
237
+ intact. Assistant messages and Loggie's separate projection path are unaffected.
238
+ Raw session events and workspace memory files are never rewritten.
239
+ Attachment matching has a fixed work budget; oversized or repeatedly nested/
240
+ incomplete envelopes leave the entire message unchanged rather than blocking sync.
241
+
242
+ The companion QMD semantic-chunking update skips only source-confirmed standalone
243
+ REM heading/marker spans and orphan closing fences. Reflections and useful text
244
+ remain searchable, with original source offsets. These are deterministic rules,
245
+ not TypeSafe judgments; audit flags never authorize automatic memory deletion.
246
+
247
+ The plugin installs its exact release-pinned QMD dependency (see
248
+ [package metadata](../package.json)). Projector/chunker version changes refresh derived
249
+ projections and embeddings on their next normal sync; the first sync may take
250
+ longer while re-embedding. No manual deletion of source memories or review tasks
251
+ is needed.
252
+
253
+ ## Memory Whisperer
254
+
255
+ Memory Whisperer is optional and **off by default**. It proactively retrieves
256
+ historical context before user-triggered turns, without changing `memory_search`
257
+ or `memory_get`. Enable it in the plugin config with an explicit corpus allowlist:
258
+
259
+ ```json
260
+ {
261
+ "memoryWhisperer": {
262
+ "enabled": true,
263
+ "corpora": ["knowledge"],
264
+ "historyMessages": 5,
265
+ "minUsefulness": 0.7,
266
+ "maxHints": 2,
267
+ "cooldownTurns": 10,
268
+ "timeoutMs": 3000
269
+ }
270
+ }
271
+ ```
272
+
273
+ Requires `hooks.allowConversationAccess: true` on the plugin entry, prompt
274
+ injection permission, and [shared TypeSafe credentials](configuration.md#shared-typesafe-credentials).
275
+ An empty allowlist is invalid when enabled; `all`, unknown names, and `skills`
276
+ are not accepted. When `sessions` is enabled for Memory Whisperer, automatic recall
277
+ can retrieve across this agent's indexed sessions. The sessions corpus's `chatTypes`
278
+ setting controls whether direct messages are included; no additional session-scope
279
+ toggle is required. Selected excerpts are sent to TypeSafe and may be injected into
280
+ any conversation using this agent. Session availability still depends on the normal
281
+ indexing/sync schedule.
282
+
283
+ The example is a plugin config fragment; `knowledge` must already be configured.
284
+ For a complete corpus example, use the [configuration profiles](configuration.md#example-profiles).
285
+
286
+ QMD searches the current request plus the last N user/assistant messages (at most
287
+ 12,000 characters), retrieving up to eight vector candidates without query expansion,
288
+ the local reranker, or a similarity-score cutoff. TypeSafe evaluates one independent
289
+ Noul question per candidate in a single request: does the excerpt add material value
290
+ beyond what the conversation already contains? Merely related, redundant,
291
+ wrong-person/project, and clearly superseded information should be rejected;
292
+ useful contradictory evidence can qualify. `minUsefulness` defaults to `0.7` and
293
+ thresholds the probability of yes, not a calibrated guarantee of accuracy.
294
+ Explicit configured thresholds are preserved. Evaluate it on your own conversations.
295
+
296
+ **Privacy and budgets:** this feature sends up to 16,000 characters of the available
297
+ user/assistant conversation, prioritizing the current request and recent messages,
298
+ plus up to eight 1,200-character excerpts, corpus names, and matched-message timestamps
299
+ when available to `api.typesafe.ai`. The same `messageTimestamp` accompanies the
300
+ injected hint: it records when something was said, without inferring event dates.
301
+ Session excerpts retain a complete turn or message when it fits,
302
+ otherwise the complete matched chunk. Chunks exceeding the excerpt budget are
303
+ skipped, never sliced; ordinary `memory_search` is unchanged.
304
+ It does not fetch a complete historical transcript; the host may
305
+ already have compacted the available context. Truncation is marked in the judge's
306
+ input. System messages, thinking blocks, images, and tool-result messages are omitted;
307
+ anything quoted in ordinary user/assistant text can still be transmitted.
308
+
309
+ At most two qualifying excerpts are injected verbatim with source references and
310
+ historical/untrusted-data framing. Excerpts are deduplicated by normalized content
311
+ and overlapping source lines; recently injected content has a ten-user-turn cooldown
312
+ by default. Cooldown state is in memory and resets on session end or Gateway restart.
313
+ The complete hint payload is capped at 5,000 characters plus a short framing paragraph.
314
+
315
+ Unlike Skill Whisperer, **disabled TypeSafe, a missing key, no qualifying hits, or any
316
+ failure means no memory hint**—there is no vector-only fallback. The overall process
317
+ has a 3-second deadline, with the shared 1.5-second TypeSafe request deadline inside it;
318
+ neither performs retries. Timed-out or superseded runs cannot inject late hints.
319
+ Already-running local QMD work may finish in the background, but does not keep the
320
+ agent waiting beyond the deadline. No new indexing, clustering, or summarization runs
321
+ are triggered by this feature beyond the memory manager's normal initialization.
322
+
323
+ ## Skill Whisperer
324
+
325
+ Skill Whisperer is an optional semantic reminder for user turns. Configure one
326
+ isolated `skills` corpus, set `skillWhisperer.enabled` to `true`, and authorize
327
+ `plugins.entries.unblock-memory.hooks.allowConversationAccess`. The feature
328
+ embeds the current prompt plus the configured number of prior user/assistant
329
+ messages, compares it with each configured skill's frontmatter `name` and
330
+ `description`. With TypeSafe enabled and a key available, the top three valid
331
+ candidates are sent to TypeSafe, without a vector-score cutoff. TypeSafe chooses
332
+ one skill or none. A "none" decision never falls back to a vector hint. Full skill
333
+ procedures do not influence routing; no skill is invoked automatically.
334
+
335
+ See [shared TypeSafe credentials](configuration.md#shared-typesafe-credentials) for key setup,
336
+ rotation and the `enabled: true` / `timeoutMs: 1500` defaults.
337
+
338
+ If TypeSafe is disabled or no key is found, selection uses the original local
339
+ vector process and `skillWhisperer.minScore`. With a key present, an API error,
340
+ invalid response, or timeout emits no hint and logs a sanitized warning; it does
341
+ not switch to vector-only selection. There are no automatic HTTP retries. Other
342
+ credential-file read errors likewise produce a warning and no hint.
343
+
344
+ **Privacy:** enabled TypeSafe selection sends up to 12,000 characters of current
345
+ prompt/recent user-assistant text, plus the shortlisted names/descriptions, to
346
+ `api.typesafe.ai`. Source-path fields, full skill procedures, tool-result messages,
347
+ and system messages are excluded; dossiers and ordinary memory files are not read
348
+ for this call. Material already quoted in user/assistant text can still be included.
349
+ Disable `typesafe.enabled` to keep Skill Whisperer entirely local. The pinned model
350
+ is `jev-1.13.0`.
351
+
352
+ The defaults use five prior messages, a vector-only score threshold of `0.5`,
353
+ and a ten-turn cooldown. A skill is cooling down after either a suggestion or a
354
+ successful direct `read` of its indexed `SKILL.md`. When the selected
355
+ skill is cooling down, no hint is emitted; Skill Whisperer does not fall through
356
+ to a weaker match. Cooldown state is per session and intentionally resets with
357
+ the Gateway. Shell-command reads are not tracked.
358
+
359
+ The `skills` corpus shares the existing QMD store and warm embedding model but
360
+ is private to Skill Whisperer: it is excluded from ordinary `memory_search`
361
+ (including `corpora: ["all"]`), `memory_get`, clustering, and memory-maintenance
362
+ tasks. Paths are explicit by design; the plugin does not reconstruct
363
+ OpenClaw's effective skill inventory from `openclaw.json`. Configured skill
364
+ globs follow symlinked directories, including OpenClaw's `plugin-skills`
365
+ directory.
366
+
367
+ ## Review and diagnostics
368
+
369
+ - `memory_diagnostics` reports credential **availability only**, per-agent process-local
370
+ whisperer counters, projection version, old indexed-session projection count, and
371
+ embedding readiness. Counters are bounded to 100 agents and reset on restart.
372
+ This is not a complete people/response feature-status report; it makes no TypeSafe
373
+ request, but can initialize the memory manager/index on first use.
374
+ No prompts, excerpts, paths, keys, or provider error bodies enter these counters.
375
+ Parser cleanup/budget-skip counts are persisted with the latest completed
376
+ `memory_sync_status`; unchanged sessions are not counted again. QMD structural
377
+ omission counts cover this manager's embedding passes, not the whole corpus.
378
+ - Quality-audit groups distinguish `preserve_evidence_repair`, `inspect_scaffolding`,
379
+ and `context_review`, reusing cached noise/evidence judgments without another call.
380
+ Evidence-preserving repair tasks sort first. Maintenance tasks expose indexed
381
+ fingerprint presence; `not_present_in_index` is **not** a verified repair and
382
+ never resolves or deletes the task. Chunk boundaries may simply have changed.
383
+ - `memory_review_cluster` uses the existing `qualityAudit` opt-in/corpus allowlist.
384
+ It judges up to three representative and three low-membership members, deduplicates
385
+ the sample, and skips unapproved or >2,000-character chunks whole. Repeated defect
386
+ labels are investigation leads only. Stale/changed samples are rejected; useful
387
+ or uncertain members are retained. No tasks or sources are modified.
388
+ - `memory_review_claim` accepts one atomic claim (up to 2,000 characters) and 1–3
389
+ citations `{path, from, lines}`. It reads approved indexed evidence itself (at
390
+ most 6,000 characters), returns supports/contradicts/insufficient_evidence with
391
+ confidence and source hashes, and never writes or authorizes a write. Support
392
+ below 0.9 confidence is marked for review. This threshold is provisional, not a
393
+ guarantee of truth; read original evidence and verify current-state claims.
394
+
395
+ Optional plugin config fragment (corpora must already be configured):
396
+
397
+ ```json
398
+ {
399
+ "evidenceReview": { "enabled": true, "corpora": ["memory", "knowledge", "sessions"] },
400
+ "memoryWhisperer": {
401
+ "enabled": true, "corpora": ["memory", "knowledge"], "complementaryHints": true
402
+ }
403
+ }
404
+ ```
405
+
406
+ Both additions default off. Claim review sends the proposed claim and approved
407
+ source excerpts to TypeSafe; cluster review sends approved sampled excerpts.
408
+ Complementary hints use one extra bounded call over at most four already-useful
409
+ candidates (six directional comparisons). Only redundancy probability >=0.9
410
+ removes a hint; distinct evidence and contradictions should remain. Provider errors
411
+ retain baseline hints, while the existing total turn deadline/cancellation still
412
+ suppresses late results. Missing keys or disabled TypeSafe never enable these calls.
413
+ The retrieval corpus/session boundaries are unchanged.
414
+
415
+ ## Memory quality audit
416
+
417
+ `memory_audit_quality` is an on-demand, source-read-only audit. TypeSafe flags likely
418
+ ingestion noise for agent investigation; it never deletes, rewrites, or suppresses
419
+ memory. Enable it with explicit approval for the corpora sent to TypeSafe:
420
+
421
+ ```json
422
+ {
423
+ "qualityAudit": {
424
+ "enabled": true,
425
+ "corpora": ["memory", "knowledge"],
426
+ "minNoise": 0.8
427
+ }
428
+ }
429
+ ```
430
+
431
+ This is a plugin config fragment; both corpora must already be configured.
432
+ Off by default. Uses the shared TypeSafe credentials and request timeout. Missing
433
+ credentials or disabled TypeSafe produces no audit. Approval includes transmission
434
+ of full eligible chunks and visibility of findings to all audiences using the agent.
435
+ Unlike Memory Whisperer, approving `sessions` includes **all indexed sessions** in
436
+ that corpus, including configured direct conversations. Only approve that when intended.
437
+
438
+ Call with `{ "limit": 10 }` (maximum 20 indexed chunk occurrences per page), then
439
+ pass the returned `next` as `after` until `done` is true. A `partial` result preserves
440
+ the completed cursor; retry there, or from the beginning if no cursor exists. This
441
+ is not a full-document audit: unindexed content is not scanned. Chunks over 6,000
442
+ characters are counted as skipped, not silently truncated. No clustering is required.
443
+
444
+ Two independent Noul questions distinguish ingestion noise from identifiable useful
445
+ evidence. High values for both can indicate valuable content trapped in a wrapper.
446
+ Low evidence alone does not create a junk finding. JSON, logs, code, terse facts,
447
+ historical records, and missing context are not automatically defects. Empty chunks
448
+ are detected locally. A JSON string that decodes to a message envelope is also
449
+ flagged as a possible double-encoding defect, even when its content is useful.
450
+ An ordinary JSON message object is not flagged from its shape alone. These are
451
+ review clues, never verdicts about whether the information should be kept.
452
+
453
+ At most four unique chunks (24,000 characters) and their source kinds are sent in
454
+ one request, without conversation context or source paths. Requests do not retry
455
+ automatically and stop starting new work after a 30-second audit deadline; existing
456
+ manager initialization/indexing may finish later. Judgments are cached in the
457
+ curation database by content, source kind, model and question version. A rescan from
458
+ the beginning reuses cached results, including after corpus/index changes. Changes
459
+ behind a page cursor are picked up on the next rescan.
460
+
461
+ Suspect chunks become `quality_review` tasks in `memory_list_maintenance_tasks`.
462
+ The audit returns page-local groups by configured source and suspected issue,
463
+ with up to three examples each, not a claim that a whole cluster is defective.
464
+ Findings include source references, bounded previews, probabilities and content
465
+ fingerprints. Reviewed tasks are not reopened for unchanged content. The curator
466
+ inspects the original source and ingestion path, proposes or performs authorized
467
+ repairs, and verifies the resulting source/index before resolving with a required
468
+ note. Prefer repairing a common extractor or inclusion rule over many symptoms;
469
+ never manually edit generated session projections. Thresholds need evaluation on
470
+ your data; model probability is not proof of a defect.
471
+
472
+ ## Memory analysis
473
+
474
+ Analysis is opt-in. Core indexing, `memory_search`, and `memory_get` need only
475
+ Unblock Memory and its automatically installed QMD dependency. To enable
476
+ clustering, install the public
477
+ [`unblock-cluster`](https://github.com/unblocklabs-ai/unblock-cluster) worker once
478
+ on the same host:
479
+
480
+ ```bash
481
+ git clone https://github.com/unblocklabs-ai/unblock-cluster.git
482
+ cd unblock-cluster
483
+ python3 -m venv .venv
484
+ .venv/bin/python -m pip install -r requirements-analysis.txt
485
+ ```
486
+
487
+ Set `analysis.executable` to the absolute path of
488
+ `bin/unblock-memory-analysis` in that checkout. One worker installation can
489
+ serve every agent on the host. The plugin invokes it directly with
490
+ `--db <the agent's known index path>`, the plugin's non-skill collection IDs,
491
+ and, when requested, a validated `--config-json <clustering options>` payload.
492
+ Agents cannot choose a database, executable, collection, shell command, or
493
+ arbitrary arguments.
494
+
495
+ Without the worker, `memory_list_clusters` reports that memory has not been
496
+ analyzed and `memory_recluster` reports that analysis is unavailable. Ordinary
497
+ memory search and reads continue to work.
498
+
499
+ The analysis worker reads QMD's existing semantic vectors and writes only
500
+ derived results into four namespaced tables in that same `index.sqlite`:
501
+
502
+ - `memory_analysis_runs`
503
+ - `memory_analysis_clusters`
504
+ - `memory_analysis_memberships`
505
+ - `memory_analysis_duplicate_occurrences`
506
+
507
+ Unblock Memory exposes:
508
+
509
+ - `memory_list_clusters` to cheaply list current clusters and report whether the
510
+ retained analysis is stale
511
+ - `memory_recluster` to explicitly rebuild clusters when the list is missing or stale
512
+ - `memory_fetch_cluster` to return a sorted, paginated selection of QMD chunks
513
+ for a short `clusterId` returned by `memory_list_clusters`
514
+
515
+ `memory_recluster` optionally accepts UMAP controls (`method`, components,
516
+ neighbors, and minimum distance), HDBSCAN controls (minimum cluster size,
517
+ minimum samples, selection method and epsilon, and single-cluster behavior),
518
+ and a deterministic seed. Omitting them uses the worker's defaults.
519
+
520
+ `memory_fetch_cluster` accepts `topK` (1–50), a zero-based `offset`, and
521
+ `sort`: `representative` (the default), `score_desc`, `score_asc`, `date_desc`,
522
+ or `date_asc`. Score is cluster membership probability for normal clusters and
523
+ outlier score for noise. Each member reports raw `sourceModifiedAt` separately
524
+ from `eventTime` and `eventTimeBasis`. Session start times and dated memory paths
525
+ resolve programmatically; reviewed annotations resolve otherwise ambiguous
526
+ chunks or whole documents. Date sorting uses resolved event time when available
527
+ and the clearly labeled source modification time only as a fallback. Responses
528
+ include page totals and the next offset when more members remain.
529
+
530
+ A chronological cluster read creates a coalesced maintenance proposal only for
531
+ returned documents whose event time remains ambiguous; it does not scan the
532
+ whole corpus for chores. Persisted exact-duplicate analysis can likewise create
533
+ review proposals for non-session Markdown. `memory_list_maintenance_tasks`
534
+ returns at most ten tasks, while `memory_update_maintenance_task` can resolve,
535
+ defer, or mark one irrelevant and optionally attach a supported event date.
536
+ For duplicate proposals, defer confirmed cleanup until the source change is
537
+ complete, mark intentional repetition irrelevant, and resolve only completed
538
+ work. These tools never edit or delete source Markdown. Duplicate cleanup
539
+ remains a reviewed source change outside the maintenance tool, and generated
540
+ session projections must never be edited directly.
541
+
542
+ Member excerpts are capped at 2 KB each and 12 KB across a response; source
543
+ aliases are capped at five per member and 50 across a response. These budgets
544
+ are shared across the page so every returned member receives a useful excerpt
545
+ and at least one source path, including a full 50-member page.
546
+
547
+ If indexing changes content or vectors, the previous derived analysis is kept
548
+ and marked stale. Cluster reads include the analysis timestamp, stale timestamp,
549
+ and a hint to call `memory_recluster`; unavailable chunks reduce `availableSize`
550
+ without copying canonical text into analysis tables. A no-op sync stays fresh.
551
+ A failed rebuild leaves the stale result intact, while a successful rebuild
552
+ atomically replaces it. Analysis is never scheduled automatically. If the worker
553
+ is absent or fails, `memory_search` and `memory_get` continue to work.
554
+
555
+ ## Curating knowledge
556
+
557
+ The plugin bundles the `memory-curator` skill for turning useful clusters into
558
+ durable knowledge. It becomes available when the plugin is enabled. If the
559
+ agent has an explicit skill allowlist, include `memory-curator`.
560
+
561
+ Keep maintained knowledge outside `memory/**` so each file belongs to only one
562
+ corpus. Use stable topic files updated in place:
563
+
564
+ ```text
565
+ knowledge/
566
+ ├── fleet.md
567
+ ├── people/
568
+ │ └── rico.md
569
+ └── projects/
570
+ └── unblock-memory.md
571
+ ```
572
+
573
+ Knowledge is the agent's maintained, current understanding of its unique world:
574
+ facts such as fleet membership, local decisions and preferences, assessments,
575
+ and explicit uncertainty that would be expensive to reconstruct from scattered
576
+ history. Each claim should carry its own epistemic qualification so it remains
577
+ honest when semantic chunking retrieves it alone. Remove stale conclusions
578
+ instead of preserving history, changelogs, or `Supersedes` passages in the same
579
+ file; raw memory and sessions retain the evidence history.
580
+
581
+ Public or vendor-owned facts, generic command syntax, and behavior likely to
582
+ change with third-party releases should normally be looked up from the current
583
+ authoritative source. A local policy or deliberate divergence may belong in
584
+ knowledge, but the local decision—not copied generic documentation—is the
585
+ durable content.
586
+
587
+ For a manual run, ask the agent:
588
+
589
+ ```text
590
+ Use $memory-curator to review my memory clusters and curate any durable updates.
591
+ ```
592
+
593
+ For recurring curation, use an OpenClaw automation with the same thin message:
594
+
595
+ ```text
596
+ Use $memory-curator to run the scheduled memory curation cycle.
597
+ ```
598
+
599
+ The skill treats a cluster as an incomplete attention signal. It frames the
600
+ question raised, uses representative, score, and chronological views as useful,
601
+ searches existing knowledge and adjacent corpora, and investigates live systems,
602
+ files, documentation, or the web when those are better evidence. It then updates
603
+ a stable knowledge topic or correctly writes nothing. Its own writes are indexed
604
+ for the next cycle; it does not recluster recursively in the same run.
605
+
606
+ Existing `unblock-qmd` indexes are derived caches and may be left in place;
607
+ Unblock Memory rebuilds its own index from configured corpora.