@nowledge/openclaw-nowledge-mem 0.8.18 → 0.8.21
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/CHANGELOG.md +22 -0
- package/README.md +8 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +4 -6
- package/src/context-engine.js +13 -3
- package/src/hooks/recall.js +5 -2
- package/src/index.js +27 -14
- package/src/tools/status.js +68 -26
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to the Nowledge Mem OpenClaw plugin will be documented in th
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## [0.8.21] - 2026-04-29
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- ClawHub installs no longer fail archive verification because `.clawhubignore` is now excluded from the published package manifest as well as the archive.
|
|
12
|
+
|
|
13
|
+
## [0.8.20] - 2026-04-27
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- ClawHub packages now exclude local `node_modules/`, preventing unpublished development artifacts from entering the security scan.
|
|
18
|
+
- Remote HTTP requests no longer place API keys in URL query strings. The plugin keeps credentials in `Authorization` and `X-NMEM-API-Key` headers only.
|
|
19
|
+
|
|
20
|
+
## [0.8.19] - 2026-04-21
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- `corpusSupplement` fallback now preserves the user's configured intent when host registration is unavailable. The plugin keeps its own recall path active instead of silently mutating config state.
|
|
25
|
+
- `nowledge_mem_status` now reports `corpusSupplement` as configured vs runtime-active, including fallback reason details when OpenClaw does not expose supplement registration in the current runtime.
|
|
26
|
+
- Context Engine recall now follows **runtime supplement activation** instead of static config. If `corpusSupplement` is configured but host registration fails, prompt-time recall no longer gets skipped.
|
|
27
|
+
- `nowledge_mem_status` no longer claims prompt-time fallback recall when `sessionContext=false`; status text now reflects the real runtime path.
|
|
28
|
+
|
|
7
29
|
## [0.8.18] - 2026-04-20
|
|
8
30
|
|
|
9
31
|
### Fixed
|
package/README.md
CHANGED
|
@@ -428,6 +428,14 @@ No config is required for a normal npm install. The defaults work for local mode
|
|
|
428
428
|
|
|
429
429
|
To change settings, use the OpenClaw plugin settings UI. Changes take effect on restart.
|
|
430
430
|
|
|
431
|
+
## Customize without editing the plugin
|
|
432
|
+
|
|
433
|
+
OpenClaw does not currently have a separate user-owned instruction file in this integration contract.
|
|
434
|
+
|
|
435
|
+
- Use the OpenClaw plugin settings UI for supported behavior changes such as `sessionContext`, digest controls, remote credentials, and space selection.
|
|
436
|
+
- If you need additional behavioral guidance, customize OpenClaw's own prompt or agent configuration surface.
|
|
437
|
+
- Do not patch the installed plugin source under the OpenClaw extensions directory.
|
|
438
|
+
|
|
431
439
|
| Key | Type | Default | Description |
|
|
432
440
|
|-----|------|---------|-------------|
|
|
433
441
|
| `sessionContext` | boolean | `false` | Inject Working Memory + relevant memories at prompt time |
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw-nowledge-mem",
|
|
3
3
|
"name": "Nowledge Mem",
|
|
4
4
|
"description": "Cross-tool knowledge graph memory for OpenClaw with Working Memory, graph search, and session distillation.",
|
|
5
|
-
"version": "0.8.
|
|
5
|
+
"version": "0.8.21",
|
|
6
6
|
"kind": ["memory", "context-engine"],
|
|
7
7
|
"skills": [
|
|
8
8
|
"skills/memory-guide"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nowledge/openclaw-nowledge-mem",
|
|
3
3
|
"displayName": "Nowledge Mem for OpenClaw",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.21",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Nowledge Mem memory plugin for OpenClaw, local-first personal knowledge base",
|
|
7
7
|
"author": {
|
package/src/client.js
CHANGED
|
@@ -137,16 +137,15 @@ export class NowledgeMemClient {
|
|
|
137
137
|
const timer = setTimeout(() => controller.abort(), timeout);
|
|
138
138
|
const scopedPath = this._withSpaceQuery(path);
|
|
139
139
|
const scopedBody = this._withSpaceBody(body);
|
|
140
|
-
const url = `${this.getApiBaseUrl()}${scopedPath}`;
|
|
141
140
|
try {
|
|
142
|
-
const
|
|
141
|
+
const requestBody =
|
|
142
|
+
scopedBody === undefined ? undefined : JSON.stringify(scopedBody);
|
|
143
|
+
const response = await fetch(`${this.getApiBaseUrl()}${scopedPath}`, {
|
|
143
144
|
method,
|
|
144
145
|
headers: this.getApiHeaders(),
|
|
145
|
-
body:
|
|
146
|
-
scopedBody === undefined ? undefined : JSON.stringify(scopedBody),
|
|
146
|
+
body: requestBody,
|
|
147
147
|
signal: controller.signal,
|
|
148
148
|
});
|
|
149
|
-
|
|
150
149
|
const text = await response.text();
|
|
151
150
|
let data = {};
|
|
152
151
|
try {
|
|
@@ -154,7 +153,6 @@ export class NowledgeMemClient {
|
|
|
154
153
|
} catch {
|
|
155
154
|
data = { raw: text };
|
|
156
155
|
}
|
|
157
|
-
|
|
158
156
|
if (!response.ok) {
|
|
159
157
|
const detail =
|
|
160
158
|
typeof data?.detail === "string"
|
package/src/context-engine.js
CHANGED
|
@@ -166,9 +166,15 @@ function getSessionState(key) {
|
|
|
166
166
|
* @param {import('./client.js').NowledgeMemClient} client
|
|
167
167
|
* @param {object} cfg Parsed plugin config
|
|
168
168
|
* @param {object} logger OpenClaw logger
|
|
169
|
+
* @param {object} runtime Runtime state helpers supplied by register()
|
|
169
170
|
* @returns {() => object} Factory that creates the engine instance
|
|
170
171
|
*/
|
|
171
|
-
export function createNowledgeMemContextEngineFactory(
|
|
172
|
+
export function createNowledgeMemContextEngineFactory(
|
|
173
|
+
client,
|
|
174
|
+
cfg,
|
|
175
|
+
logger,
|
|
176
|
+
runtime = {},
|
|
177
|
+
) {
|
|
172
178
|
return () => {
|
|
173
179
|
ceState.active = true;
|
|
174
180
|
logger.info("nowledge-mem: context engine activated");
|
|
@@ -242,8 +248,12 @@ export function createNowledgeMemContextEngineFactory(client, cfg, logger) {
|
|
|
242
248
|
}
|
|
243
249
|
|
|
244
250
|
// 3. Recalled memories (when sessionContext enabled)
|
|
245
|
-
//
|
|
246
|
-
|
|
251
|
+
// Skip only when corpus supplement is actually active at runtime.
|
|
252
|
+
const corpusSupplementActive =
|
|
253
|
+
typeof runtime.isCorpusSupplementActive === "function"
|
|
254
|
+
? runtime.isCorpusSupplementActive() === true
|
|
255
|
+
: cfg.corpusSupplement === true;
|
|
256
|
+
if (cfg.sessionContext && !corpusSupplementActive) {
|
|
247
257
|
const query = buildAssembleSearchQuery(prompt, messages);
|
|
248
258
|
if (query) {
|
|
249
259
|
try {
|
package/src/hooks/recall.js
CHANGED
|
@@ -177,13 +177,16 @@ export function buildRecalledKnowledgeBlock(
|
|
|
177
177
|
* When the context engine is active, this hook is a no-op —
|
|
178
178
|
* assemble() handles recall via systemPromptAddition.
|
|
179
179
|
*/
|
|
180
|
-
export function buildRecallHandler(client, cfg, logger) {
|
|
180
|
+
export function buildRecallHandler(client, cfg, logger, runtime = {}) {
|
|
181
181
|
const minScore = (cfg.recallMinScore ?? 0) / 100; // config is 0-100, API is 0-1
|
|
182
182
|
|
|
183
183
|
// When corpus supplement is active, memory-core's recall pipeline searches
|
|
184
184
|
// Nowledge Mem via the supplement. We still inject Working Memory (only we
|
|
185
185
|
// know about WM), but skip our own search-based recall to avoid duplicates.
|
|
186
|
-
const skipSearchRecall =
|
|
186
|
+
const skipSearchRecall =
|
|
187
|
+
typeof runtime.skipSearchRecall === "boolean"
|
|
188
|
+
? runtime.skipSearchRecall
|
|
189
|
+
: cfg.corpusSupplement;
|
|
187
190
|
|
|
188
191
|
return async (event) => {
|
|
189
192
|
if (ceState.active) return;
|
package/src/index.js
CHANGED
|
@@ -80,6 +80,8 @@ export default {
|
|
|
80
80
|
const pluginsAllow = api.config?.plugins?.allow;
|
|
81
81
|
let contextEngineRegistered = false;
|
|
82
82
|
let contextEngineRegistrationError = null;
|
|
83
|
+
let corpusSupplementActive = false;
|
|
84
|
+
let corpusSupplementRegistrationError = null;
|
|
83
85
|
|
|
84
86
|
// --- Context Engine registration ---
|
|
85
87
|
// When the user sets `plugins.slots.contextEngine: "nowledge-mem"` —
|
|
@@ -94,6 +96,9 @@ export default {
|
|
|
94
96
|
client,
|
|
95
97
|
cfg,
|
|
96
98
|
logger,
|
|
99
|
+
{
|
|
100
|
+
isCorpusSupplementActive: () => corpusSupplementActive,
|
|
101
|
+
},
|
|
97
102
|
);
|
|
98
103
|
for (const contextEngineId of NOWLEDGE_MEM_CONTEXT_ENGINE_IDS) {
|
|
99
104
|
api.registerContextEngine(contextEngineId, contextEngineFactory);
|
|
@@ -107,17 +112,6 @@ export default {
|
|
|
107
112
|
);
|
|
108
113
|
}
|
|
109
114
|
|
|
110
|
-
api.registerTool(
|
|
111
|
-
createStatusTool(client, logger, cfg, {
|
|
112
|
-
memorySlot,
|
|
113
|
-
contextEngineSlot,
|
|
114
|
-
contextEngineIds: NOWLEDGE_MEM_CONTEXT_ENGINE_IDS,
|
|
115
|
-
pluginsAllow,
|
|
116
|
-
contextEngineRegistered,
|
|
117
|
-
contextEngineRegistrationError,
|
|
118
|
-
}),
|
|
119
|
-
);
|
|
120
|
-
|
|
121
115
|
// --- Corpus Supplement (when memory-core is the memory slot) ---
|
|
122
116
|
// Makes Nowledge Mem's knowledge graph searchable through memory-core's
|
|
123
117
|
// recall pipeline and dreaming promotion. Memories participate in
|
|
@@ -127,18 +121,32 @@ export default {
|
|
|
127
121
|
api.registerMemoryCorpusSupplement(
|
|
128
122
|
createNowledgeMemCorpusSupplement(client, cfg, logger),
|
|
129
123
|
);
|
|
124
|
+
corpusSupplementActive = true;
|
|
130
125
|
logger.info(
|
|
131
126
|
"nowledge-mem: registered as MemoryCorpusSupplement for memory-core recall",
|
|
132
127
|
);
|
|
133
128
|
} catch (err) {
|
|
134
129
|
// OpenClaw < corpus supplement support — fall back to normal recall
|
|
135
|
-
|
|
130
|
+
corpusSupplementRegistrationError = String(err);
|
|
136
131
|
logger.info?.(
|
|
137
132
|
`nowledge-mem: corpus supplement unavailable, falling back to normal recall (${err})`,
|
|
138
133
|
);
|
|
139
134
|
}
|
|
140
135
|
}
|
|
141
136
|
|
|
137
|
+
api.registerTool(
|
|
138
|
+
createStatusTool(client, logger, cfg, {
|
|
139
|
+
memorySlot,
|
|
140
|
+
contextEngineSlot,
|
|
141
|
+
contextEngineIds: NOWLEDGE_MEM_CONTEXT_ENGINE_IDS,
|
|
142
|
+
pluginsAllow,
|
|
143
|
+
contextEngineRegistered,
|
|
144
|
+
contextEngineRegistrationError,
|
|
145
|
+
corpusSupplementActive,
|
|
146
|
+
corpusSupplementRegistrationError,
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
|
|
142
150
|
// --- Hooks ---
|
|
143
151
|
// Behavioral + recall hooks defer to the active CE to avoid duplicate
|
|
144
152
|
// prompt injection. Capture hooks remain enabled as a backstop because
|
|
@@ -151,7 +159,12 @@ export default {
|
|
|
151
159
|
|
|
152
160
|
// Session context: inject Working Memory + recalled memories at prompt time.
|
|
153
161
|
if (cfg.sessionContext) {
|
|
154
|
-
api.on(
|
|
162
|
+
api.on(
|
|
163
|
+
"before_prompt_build",
|
|
164
|
+
buildRecallHandler(client, cfg, logger, {
|
|
165
|
+
skipSearchRecall: corpusSupplementActive,
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
155
168
|
}
|
|
156
169
|
|
|
157
170
|
// Session digest: capture threads + LLM distillation at lifecycle events.
|
|
@@ -182,7 +195,7 @@ export default {
|
|
|
182
195
|
|
|
183
196
|
const remoteMode = !isDefaultApiUrl(cfg.apiUrl);
|
|
184
197
|
logger.info(
|
|
185
|
-
`nowledge-mem: initialized (memorySlotSelected=${memorySlotSelected}, context=${cfg.sessionContext}, digest=${cfg.sessionDigest},
|
|
198
|
+
`nowledge-mem: initialized (memorySlotSelected=${memorySlotSelected}, context=${cfg.sessionContext}, digest=${cfg.sessionDigest}, corpusConfigured=${cfg.corpusSupplement}, corpusActive=${corpusSupplementActive}, mode=${remoteMode ? `remote → ${cfg.apiUrl}` : "local"}, contextEngineIds=${NOWLEDGE_MEM_CONTEXT_ENGINE_ID}|${api.id})`,
|
|
186
199
|
);
|
|
187
200
|
},
|
|
188
201
|
};
|
package/src/tools/status.js
CHANGED
|
@@ -67,35 +67,62 @@ export function createStatusTool(client, _logger, cfg, runtimeInfo = {}) {
|
|
|
67
67
|
// 0b. Memory slot check — show current mode and options
|
|
68
68
|
const memorySlot = runtimeInfo.memorySlot;
|
|
69
69
|
const contextEngineSlot = runtimeInfo.contextEngineSlot;
|
|
70
|
-
const contextEngineRegistered =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
70
|
+
const contextEngineRegistered =
|
|
71
|
+
runtimeInfo.contextEngineRegistered === true;
|
|
72
|
+
const contextEngineRegistrationError =
|
|
73
|
+
typeof runtimeInfo.contextEngineRegistrationError === "string" &&
|
|
74
|
+
runtimeInfo.contextEngineRegistrationError.trim()
|
|
75
|
+
? runtimeInfo.contextEngineRegistrationError.trim()
|
|
76
|
+
: null;
|
|
77
|
+
const corpusSupplementConfigured = cfg.corpusSupplement === true;
|
|
78
|
+
const corpusSupplementActive =
|
|
79
|
+
runtimeInfo.corpusSupplementActive === true;
|
|
80
|
+
const corpusSupplementRegistrationError =
|
|
81
|
+
typeof runtimeInfo.corpusSupplementRegistrationError === "string" &&
|
|
82
|
+
runtimeInfo.corpusSupplementRegistrationError.trim()
|
|
83
|
+
? runtimeInfo.corpusSupplementRegistrationError.trim()
|
|
84
|
+
: null;
|
|
85
|
+
details.memorySlot = memorySlot ?? "(unknown)";
|
|
86
|
+
details.corpusSupplementConfigured = corpusSupplementConfigured;
|
|
87
|
+
details.corpusSupplementActive = corpusSupplementActive;
|
|
88
|
+
if (corpusSupplementRegistrationError) {
|
|
89
|
+
details.corpusSupplementRegistrationError =
|
|
90
|
+
corpusSupplementRegistrationError;
|
|
91
|
+
}
|
|
92
|
+
const ceSlot =
|
|
93
|
+
typeof contextEngineSlot === "string" && contextEngineSlot.trim()
|
|
94
|
+
? contextEngineSlot.trim()
|
|
95
|
+
: "legacy";
|
|
96
|
+
details.contextEngineSlot = ceSlot;
|
|
97
|
+
details.contextEngineRegistered = contextEngineRegistered;
|
|
83
98
|
if (memorySlot && memorySlot !== "openclaw-nowledge-mem") {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
lines.push(
|
|
87
|
-
`Memory slot: "${memorySlot}" + corpus supplement active`,
|
|
88
|
-
);
|
|
99
|
+
if (corpusSupplementConfigured && corpusSupplementActive) {
|
|
100
|
+
lines.push(`Memory slot: "${memorySlot}" + corpus supplement active`);
|
|
89
101
|
lines.push(
|
|
90
102
|
" Nowledge Mem feeds into memory-core's recall and dreaming pipeline. All tools available.",
|
|
91
103
|
);
|
|
92
|
-
} else {
|
|
93
|
-
lines.push(
|
|
94
|
-
`ℹ Memory slot: "${memorySlot}"`,
|
|
95
|
-
);
|
|
104
|
+
} else if (corpusSupplementConfigured && !corpusSupplementActive) {
|
|
105
|
+
lines.push(`ℹ Memory slot: "${memorySlot}"`);
|
|
96
106
|
lines.push(
|
|
97
|
-
"
|
|
107
|
+
" corpusSupplement is configured, but host registration is unavailable in this runtime.",
|
|
98
108
|
);
|
|
109
|
+
if (cfg.sessionContext) {
|
|
110
|
+
lines.push(
|
|
111
|
+
" Fallback active: Nowledge Mem still injects Working Memory and uses its own recall path.",
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
lines.push(
|
|
115
|
+
" Fallback: Nowledge Mem tools remain available. Enable sessionContext for prompt-time Working Memory and recall.",
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (corpusSupplementRegistrationError) {
|
|
119
|
+
lines.push(
|
|
120
|
+
` Supplement registration detail: ${corpusSupplementRegistrationError}`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
lines.push(`ℹ Memory slot: "${memorySlot}"`);
|
|
125
|
+
lines.push(" Two options:");
|
|
99
126
|
lines.push(
|
|
100
127
|
' - Full mode: set plugins.slots.memory to "openclaw-nowledge-mem" for the complete tool surface',
|
|
101
128
|
);
|
|
@@ -113,7 +140,9 @@ export function createStatusTool(client, _logger, cfg, runtimeInfo = {}) {
|
|
|
113
140
|
isNowledgeMemContextEngineSlot(ceSlot) && contextEngineRegistered;
|
|
114
141
|
const captureMode = !cfg.sessionDigest
|
|
115
142
|
? "disabled"
|
|
116
|
-
: ceActive
|
|
143
|
+
: ceActive
|
|
144
|
+
? "context-engine+hooks"
|
|
145
|
+
: "hooks";
|
|
117
146
|
details.captureMode = captureMode;
|
|
118
147
|
if (ceActive) {
|
|
119
148
|
if (ceSlot === NOWLEDGE_MEM_CONTEXT_ENGINE_COMPAT_ALIAS) {
|
|
@@ -124,7 +153,9 @@ export function createStatusTool(client, _logger, cfg, runtimeInfo = {}) {
|
|
|
124
153
|
` OpenClaw selected the plugin id for the context-engine slot. ${NOWLEDGE_MEM_CONTEXT_ENGINE_ID} remains the canonical manual setting.`,
|
|
125
154
|
);
|
|
126
155
|
} else {
|
|
127
|
-
lines.push(
|
|
156
|
+
lines.push(
|
|
157
|
+
`Context Engine slot: ${NOWLEDGE_MEM_CONTEXT_ENGINE_ID} (active)`,
|
|
158
|
+
);
|
|
128
159
|
}
|
|
129
160
|
lines.push(
|
|
130
161
|
" Thread capture runs through Context Engine afterTurn with hook fallback (agent_end, after_compaction, before_reset).",
|
|
@@ -146,7 +177,9 @@ export function createStatusTool(client, _logger, cfg, runtimeInfo = {}) {
|
|
|
146
177
|
" The slot points to Nowledge Mem, but Context Engine registration is not active in this runtime. Thread capture is using hooks only.",
|
|
147
178
|
);
|
|
148
179
|
if (contextEngineRegistrationError) {
|
|
149
|
-
lines.push(
|
|
180
|
+
lines.push(
|
|
181
|
+
` CE registration detail: ${contextEngineRegistrationError}`,
|
|
182
|
+
);
|
|
150
183
|
}
|
|
151
184
|
} else {
|
|
152
185
|
lines.push(
|
|
@@ -268,6 +301,14 @@ export function createStatusTool(client, _logger, cfg, runtimeInfo = {}) {
|
|
|
268
301
|
lines.push(
|
|
269
302
|
` corpusSupplement: ${cfg.corpusSupplement} (${sources.corpusSupplement || "?"})`,
|
|
270
303
|
);
|
|
304
|
+
if (corpusSupplementConfigured) {
|
|
305
|
+
const fallbackText = cfg.sessionContext
|
|
306
|
+
? "configured but unavailable (fallback to plugin recall)"
|
|
307
|
+
: "configured but unavailable (sessionContext disabled)";
|
|
308
|
+
lines.push(
|
|
309
|
+
` corpusSupplement runtime: ${corpusSupplementActive ? "active" : fallbackText}`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
271
312
|
|
|
272
313
|
details.config = {
|
|
273
314
|
sessionContext: {
|
|
@@ -297,6 +338,7 @@ export function createStatusTool(client, _logger, cfg, runtimeInfo = {}) {
|
|
|
297
338
|
corpusSupplement: {
|
|
298
339
|
value: cfg.corpusSupplement,
|
|
299
340
|
source: sources.corpusSupplement,
|
|
341
|
+
active: corpusSupplementActive,
|
|
300
342
|
},
|
|
301
343
|
apiUrl: { value: cfg.apiUrl || "(local)", source: sources.apiUrl },
|
|
302
344
|
apiKey: {
|