@unblocklabs/unblock-memory 0.3.12 → 0.3.14
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 +181 -6
- package/dist/src/config.d.ts +20 -0
- package/dist/src/config.js +103 -2
- package/dist/src/contracts.d.ts +4 -0
- package/dist/src/curation.d.ts +5 -2
- package/dist/src/curation.js +38 -2
- package/dist/src/loggie-projection.d.ts +24 -0
- package/dist/src/loggie-projection.js +126 -0
- package/dist/src/manager.d.ts +36 -1
- package/dist/src/manager.js +58 -10
- package/dist/src/memory-whisperer.d.ts +15 -0
- package/dist/src/memory-whisperer.js +134 -0
- package/dist/src/plugin.js +46 -2
- package/dist/src/quality-audit.d.ts +60 -0
- package/dist/src/quality-audit.js +151 -0
- package/dist/src/session-projector.js +41 -2
- package/dist/src/session-sync.js +1 -1
- package/dist/src/skill-whisperer.d.ts +1 -1
- package/dist/src/skill-whisperer.js +45 -19
- package/dist/src/typesafe.d.ts +47 -0
- package/dist/src/typesafe.js +228 -0
- package/dist/src/whisperer-context.d.ts +13 -0
- package/dist/src/whisperer-context.js +35 -0
- package/openclaw.plugin.json +67 -1
- package/package.json +2 -2
- package/skills/memory-curator/SKILL.md +26 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Only visible user/assistant text; never system, tool, image, or thinking blocks. */
|
|
2
|
+
export function messageText(message) {
|
|
3
|
+
if (!message || typeof message !== "object" || !("role" in message) || !("content" in message) ||
|
|
4
|
+
(message.role !== "user" && message.role !== "assistant"))
|
|
5
|
+
return undefined;
|
|
6
|
+
const text = typeof message.content === "string" ? message.content.trim() :
|
|
7
|
+
Array.isArray(message.content) ? message.content.flatMap((part) => {
|
|
8
|
+
return part && typeof part === "object" && "type" in part && part.type === "text" &&
|
|
9
|
+
"text" in part && typeof part.text === "string" ? [part.text] : [];
|
|
10
|
+
}).join("\n").trim() : "";
|
|
11
|
+
return text ? { role: message.role, text } : undefined;
|
|
12
|
+
}
|
|
13
|
+
export function memoryConversation(prompt, messages) {
|
|
14
|
+
const currentRequest = prompt.trim().slice(-16_000);
|
|
15
|
+
let remaining = 16_000 - currentRequest.length;
|
|
16
|
+
let truncated = currentRequest.length < prompt.trim().length;
|
|
17
|
+
const history = [];
|
|
18
|
+
const available = messages.flatMap(message => {
|
|
19
|
+
const parsed = messageText(message);
|
|
20
|
+
return parsed ? [parsed] : [];
|
|
21
|
+
});
|
|
22
|
+
// Hosts may include the current user message in messages as well as prompt.
|
|
23
|
+
if (available.at(-1)?.role === "user" && available.at(-1)?.text === prompt.trim())
|
|
24
|
+
available.pop();
|
|
25
|
+
for (const message of available.reverse()) {
|
|
26
|
+
if (message.text.length > remaining)
|
|
27
|
+
truncated = true;
|
|
28
|
+
if (remaining <= 0)
|
|
29
|
+
continue;
|
|
30
|
+
const content = message.text.slice(-remaining);
|
|
31
|
+
history.unshift({ role: message.role, content });
|
|
32
|
+
remaining -= content.length;
|
|
33
|
+
}
|
|
34
|
+
return { currentRequest, history, truncated };
|
|
35
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "unblock-memory",
|
|
3
3
|
"name": "Unblock Memory",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.14",
|
|
5
5
|
"description": "Indexes, retrieves, and analyzes configured workspace memory with existing QMD vectors.",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"activation": { "onStartup": true },
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"memory_recluster",
|
|
16
16
|
"memory_list_clusters",
|
|
17
17
|
"memory_fetch_cluster",
|
|
18
|
+
"memory_audit_quality",
|
|
18
19
|
"memory_list_maintenance_tasks",
|
|
19
20
|
"memory_update_maintenance_task",
|
|
20
21
|
"memory_people_inspect",
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
"memory_recluster": { "sideEffecting": true },
|
|
29
30
|
"memory_list_clusters": { "replaySafe": true },
|
|
30
31
|
"memory_fetch_cluster": { "replaySafe": true },
|
|
32
|
+
"memory_audit_quality": { "sideEffecting": true },
|
|
31
33
|
"memory_list_maintenance_tasks": { "replaySafe": true },
|
|
32
34
|
"memory_update_maintenance_task": { "sideEffecting": true },
|
|
33
35
|
"memory_people_inspect": { "replaySafe": true },
|
|
@@ -35,6 +37,23 @@
|
|
|
35
37
|
"memory_people_sync": { "sideEffecting": true, "optional": true }
|
|
36
38
|
},
|
|
37
39
|
"uiHints": {
|
|
40
|
+
"qualityAudit.enabled": {
|
|
41
|
+
"label": "Memory Quality Audit",
|
|
42
|
+
"help": "Enable an on-demand TypeSafe audit. Records review indicators only; never edits or suppresses data."
|
|
43
|
+
},
|
|
44
|
+
"qualityAudit.corpora": {
|
|
45
|
+
"label": "Approved Audit Corpora",
|
|
46
|
+
"help": "Explicit non-skill corpora approved for external TypeSafe processing and maintenance results visible to every audience using this agent. Sessions means ALL indexed sessions, not only the current conversation."
|
|
47
|
+
},
|
|
48
|
+
"typesafe.enabled": {
|
|
49
|
+
"label": "TypeSafe Ranking",
|
|
50
|
+
"help": "Enabled by default when credentials exist. Sends bounded conversation context and skill descriptions or approved memory excerpts to TypeSafe for enabled whisperers."
|
|
51
|
+
},
|
|
52
|
+
"typesafe.apiKey": { "label": "TypeSafe API Key", "sensitive": true },
|
|
53
|
+
"typesafe.apiKeyFile": {
|
|
54
|
+
"label": "TypeSafe Key File",
|
|
55
|
+
"help": "Absolute path to a plaintext API key or a dotenv file containing TYPESAFE_API_KEY. Prefer this over storing a key in config."
|
|
56
|
+
},
|
|
38
57
|
"keepEmbeddingModelWarm": {
|
|
39
58
|
"label": "Keep Embedding Model Warm",
|
|
40
59
|
"help": "Keep the QMD embedding model resident after first use. Disable to unload it after five minutes without model activity."
|
|
@@ -47,6 +66,14 @@
|
|
|
47
66
|
"label": "Skill Whisperer",
|
|
48
67
|
"help": "Suggest at most one semantically relevant configured skill before a user turn. Requires hook conversation access."
|
|
49
68
|
},
|
|
69
|
+
"memoryWhisperer.enabled": {
|
|
70
|
+
"label": "Memory Whisperer",
|
|
71
|
+
"help": "Inject up to two TypeSafe-approved historical excerpts before user turns. Requires explicit corpora, a TypeSafe key, and hook conversation access. Disabled by default."
|
|
72
|
+
},
|
|
73
|
+
"memoryWhisperer.corpora": {
|
|
74
|
+
"label": "Approved Hint Corpora",
|
|
75
|
+
"help": "Explicit non-skill corpus allowlist. Approve file corpora for all audiences using this agent and for transmission to TypeSafe. Session hits are restricted to the exact current session."
|
|
76
|
+
},
|
|
50
77
|
"people.enabled": {
|
|
51
78
|
"label": "PeopleSQL",
|
|
52
79
|
"help": "Maintain an agent-local people store. Disabled by default."
|
|
@@ -64,6 +91,16 @@
|
|
|
64
91
|
"type": "object",
|
|
65
92
|
"additionalProperties": false,
|
|
66
93
|
"properties": {
|
|
94
|
+
"qualityAudit": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"additionalProperties": false,
|
|
97
|
+
"properties": {
|
|
98
|
+
"enabled": { "type": "boolean", "default": false },
|
|
99
|
+
"corpora": { "type": "array", "items": { "type": "string", "pattern": "\\S" }, "default": [] },
|
|
100
|
+
"minNoise": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.8 }
|
|
101
|
+
},
|
|
102
|
+
"default": { "enabled": false, "corpora": [], "minNoise": 0.8 }
|
|
103
|
+
},
|
|
67
104
|
"keepEmbeddingModelWarm": {
|
|
68
105
|
"type": "boolean",
|
|
69
106
|
"default": true
|
|
@@ -175,6 +212,35 @@
|
|
|
175
212
|
"todos": { "maxOpen": 1000 }
|
|
176
213
|
}
|
|
177
214
|
},
|
|
215
|
+
"typesafe": {
|
|
216
|
+
"type": "object",
|
|
217
|
+
"additionalProperties": false,
|
|
218
|
+
"not": { "required": ["apiKey", "apiKeyFile"] },
|
|
219
|
+
"properties": {
|
|
220
|
+
"enabled": { "type": "boolean", "default": true },
|
|
221
|
+
"apiKey": { "type": "string", "pattern": "\\S" },
|
|
222
|
+
"apiKeyFile": { "type": "string", "pattern": "\\S" },
|
|
223
|
+
"timeoutMs": { "type": "integer", "minimum": 1, "maximum": 10000, "default": 1500 }
|
|
224
|
+
},
|
|
225
|
+
"default": { "enabled": true, "timeoutMs": 1500 }
|
|
226
|
+
},
|
|
227
|
+
"memoryWhisperer": {
|
|
228
|
+
"type": "object",
|
|
229
|
+
"additionalProperties": false,
|
|
230
|
+
"properties": {
|
|
231
|
+
"enabled": { "type": "boolean", "default": false },
|
|
232
|
+
"corpora": { "type": "array", "items": { "type": "string", "pattern": "\\S" }, "default": [] },
|
|
233
|
+
"historyMessages": { "type": "integer", "minimum": 0, "maximum": 50, "default": 5 },
|
|
234
|
+
"minUsefulness": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.9 },
|
|
235
|
+
"maxHints": { "type": "integer", "minimum": 1, "maximum": 2, "default": 2 },
|
|
236
|
+
"cooldownTurns": { "type": "integer", "minimum": 0, "maximum": 1000, "default": 10 },
|
|
237
|
+
"timeoutMs": { "type": "integer", "minimum": 1, "maximum": 10000, "default": 3000 }
|
|
238
|
+
},
|
|
239
|
+
"default": {
|
|
240
|
+
"enabled": false, "corpora": [], "historyMessages": 5, "minUsefulness": 0.9,
|
|
241
|
+
"maxHints": 2, "cooldownTurns": 10, "timeoutMs": 3000
|
|
242
|
+
}
|
|
243
|
+
},
|
|
178
244
|
"skillWhisperer": {
|
|
179
245
|
"type": "object",
|
|
180
246
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unblocklabs/unblock-memory",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "Workspace-native memory for OpenClaw, powered by QMD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"preflight": "npm run knip && npm run build && npm run typecheck && npm test && npm run plugin:inspect && npm run plugin:inspect:runtime && npm pack --dry-run"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@unblocklabs/qmd": "https://github.com/unblocklabs-ai/qmd/releases/download/v2.9.
|
|
38
|
+
"@unblocklabs/qmd": "https://github.com/unblocklabs-ai/qmd/releases/download/v2.9.5/unblocklabs-qmd-2.9.5.tgz",
|
|
39
39
|
"chokidar": "5.0.0",
|
|
40
40
|
"picomatch": "^4.0.5",
|
|
41
41
|
"typebox": "1.3.6"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: memory-curator
|
|
3
|
-
description: Investigate Unblock Memory clusters and maintain supported, agent-specific knowledge that would otherwise be difficult to reconstruct.
|
|
3
|
+
description: Investigate Unblock Memory clusters, audit ingestion quality, and maintain supported, agent-specific knowledge that would otherwise be difficult to reconstruct.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Memory Curator
|
|
@@ -82,6 +82,31 @@ rigid document template.
|
|
|
82
82
|
|
|
83
83
|
## Finish the cycle
|
|
84
84
|
|
|
85
|
+
### When auditing ingestion quality
|
|
86
|
+
|
|
87
|
+
Use `memory_audit_quality` for a bounded page of explicitly approved indexed
|
|
88
|
+
chunks. Continue with its `next` cursor until `done`, within the requested work
|
|
89
|
+
budget. On `partial`, retry from the returned cursor; stop and report repeated
|
|
90
|
+
provider failures. Restart without a cursor for a cached rescan after changes.
|
|
91
|
+
It does not inspect unindexed content; oversized chunks are reported as skipped.
|
|
92
|
+
|
|
93
|
+
Treat findings as indicators, not deletion decisions. High noise and high evidence
|
|
94
|
+
can mean useful content trapped in a wrapper. JSON, code, logs, terse facts and
|
|
95
|
+
historical records are not inherently junk. Grouped examples suggest a possible
|
|
96
|
+
shared ingestion cause, not proof that every file has the same defect.
|
|
97
|
+
|
|
98
|
+
Inspect the source via `memory_get` and the ingestion path. Prefer correcting an
|
|
99
|
+
extractor or corpus inclusion rule over individually cleaning many symptoms.
|
|
100
|
+
Moving or editing source files still requires the applicable authorization;
|
|
101
|
+
never manually repair generated session projections. Preserve original evidence.
|
|
102
|
+
Use the existing maintenance tools to dismiss legitimate content as `irrelevant`
|
|
103
|
+
or defer uncertain cases. A dismissal sticks to that chunk's content version.
|
|
104
|
+
Only resolve after verifying the resulting source and indexed content, recording
|
|
105
|
+
what was checked in the required resolution note. Neither the audit nor marking
|
|
106
|
+
a task resolved modifies or suppresses source data.
|
|
107
|
+
|
|
108
|
+
### General maintenance
|
|
109
|
+
|
|
85
110
|
- Do not rewrite raw memory or session projections.
|
|
86
111
|
- Review a small page from `memory_list_maintenance_tasks`. For ambiguous dates,
|
|
87
112
|
investigate supporting evidence and use `memory_update_maintenance_task` to
|