@nowledge/openclaw-nowledge-mem 0.6.12 → 0.6.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/CHANGELOG.md +12 -0
- package/CLAUDE.md +11 -0
- package/README.md +2 -0
- package/openclaw.plugin.json +13 -2
- package/package.json +1 -1
- package/src/config.js +21 -1
- package/src/hooks/capture.js +17 -7
- package/src/tools/status.js +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the Nowledge Mem OpenClaw plugin will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.6.14] - 2026-03-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Configurable thread message truncation.** New `maxThreadMessageChars` setting (200-20000, default 800) controls how many characters are preserved per captured thread message. Higher values keep more context in long conversations. Configurable via dashboard, config file, or `NMEM_MAX_THREAD_MESSAGE_CHARS` env var. Contributed by @blessonism.
|
|
10
|
+
|
|
11
|
+
## [0.6.13] - 2026-03-17
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Dashboard config: API key field now masked correctly.** The manifest `uiHints` for `apiKey` used `"secret": true` but OpenClaw's `PluginConfigUiHint` type expects `"sensitive": true`. The field now renders as a password input in the OpenClaw dashboard.
|
|
16
|
+
|
|
5
17
|
## [0.6.12] - 2026-03-15
|
|
6
18
|
|
|
7
19
|
### Added
|
package/CLAUDE.md
CHANGED
|
@@ -101,6 +101,7 @@ Optional config file at `~/.nowledge-mem/openclaw.json`. Falls through to OpenCl
|
|
|
101
101
|
| `digestMinInterval` | integer 0-86400 | `300` | `NMEM_DIGEST_MIN_INTERVAL` | Minimum seconds between session digests |
|
|
102
102
|
| `maxContextResults` | integer 1-20 | `5` | `NMEM_MAX_CONTEXT_RESULTS` | How many memories to inject at prompt time |
|
|
103
103
|
| `recallMinScore` | integer 0-100 | `0` | `NMEM_RECALL_MIN_SCORE` | Min relevance score (%) to include in auto-recall |
|
|
104
|
+
| `maxThreadMessageChars` | integer 200-20000 | `800` | `NMEM_MAX_THREAD_MESSAGE_CHARS` | Max chars per captured thread message before truncation |
|
|
104
105
|
| `apiUrl` | string | `""` | `NMEM_API_URL` | Remote server URL. Empty = local (127.0.0.1:14242) |
|
|
105
106
|
| `apiKey` | string | `""` | `NMEM_API_KEY` | API key. Never logged. |
|
|
106
107
|
|
|
@@ -156,6 +157,16 @@ nmem wm patch --heading "## Notes" --append "New note from agent"
|
|
|
156
157
|
NMEM_API_URL=https://your-server NMEM_API_KEY=key nmem status
|
|
157
158
|
```
|
|
158
159
|
|
|
160
|
+
## Version Bump Checklist
|
|
161
|
+
|
|
162
|
+
All three files must match on every release:
|
|
163
|
+
|
|
164
|
+
1. **`package.json`** — `"version"` (npm registry reads this for `npm publish`)
|
|
165
|
+
2. **`openclaw.plugin.json`** — `"version"` (OpenClaw runtime reads this at load time)
|
|
166
|
+
3. **`CHANGELOG.md`** — new `## [x.y.z] - YYYY-MM-DD` section header
|
|
167
|
+
|
|
168
|
+
After bumping, commit inside the `community/` submodule, then stage the updated submodule ref in the parent repo.
|
|
169
|
+
|
|
159
170
|
## Known Gaps / Accepted Limitations
|
|
160
171
|
|
|
161
172
|
1. **Feed API `date_from`/`date_to`** - supported. Backend filters events by YYYY-MM-DD range. CLI: `nmem f --from/--to`. Plugin: `nowledge_mem_timeline` accepts `date_from`/`date_to`.
|
package/README.md
CHANGED
|
@@ -368,6 +368,7 @@ To change settings, use the OpenClaw plugin settings UI. Changes take effect on
|
|
|
368
368
|
| `digestMinInterval` | integer | `300` | Minimum seconds between session digests for the same thread (0-86400) |
|
|
369
369
|
| `maxContextResults` | integer | `5` | Max memories to inject at prompt time (1-20, only used when sessionContext is enabled) |
|
|
370
370
|
| `recallMinScore` | integer | `0` | Min relevance score (0-100%) to include in auto-recall. 0 = include all |
|
|
371
|
+
| `maxThreadMessageChars` | integer | `800` | Maximum characters preserved per captured OpenClaw thread message before truncation |
|
|
371
372
|
| `apiUrl` | string | `""` | Remote server URL. Empty = local (`http://127.0.0.1:14242`) |
|
|
372
373
|
| `apiKey` | string | `""` | API key for remote access. Injected as `NMEM_API_KEY` env var, never logged |
|
|
373
374
|
|
|
@@ -382,6 +383,7 @@ For persistent or scripted config, create `~/.nowledge-mem/openclaw.json`:
|
|
|
382
383
|
"digestMinInterval": 300,
|
|
383
384
|
"maxContextResults": 5,
|
|
384
385
|
"recallMinScore": 0,
|
|
386
|
+
"maxThreadMessageChars": 800,
|
|
385
387
|
"apiUrl": "",
|
|
386
388
|
"apiKey": ""
|
|
387
389
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-nowledge-mem",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.14",
|
|
4
4
|
"kind": "memory",
|
|
5
5
|
"skills": ["skills/memory-guide"],
|
|
6
6
|
"uiHints": {
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"label": "Min recall score (%)",
|
|
25
25
|
"help": "Only inject memories with relevance score above this threshold (0-100). Set to 0 to include all results."
|
|
26
26
|
},
|
|
27
|
+
"maxThreadMessageChars": {
|
|
28
|
+
"label": "Max thread message chars",
|
|
29
|
+
"help": "Maximum characters preserved per captured OpenClaw thread message (200-20000). Higher values keep more of long messages in Nowledge thread history."
|
|
30
|
+
},
|
|
27
31
|
"apiUrl": {
|
|
28
32
|
"label": "Server URL (remote mode)",
|
|
29
33
|
"help": "Leave empty for local mode (default: http://127.0.0.1:14242). Set to your remote server URL for cross-device or team access. See: https://docs.nowledge.co/docs/remote-access"
|
|
@@ -31,7 +35,7 @@
|
|
|
31
35
|
"apiKey": {
|
|
32
36
|
"label": "API key (remote mode)",
|
|
33
37
|
"help": "Required when connecting to a remote server. Never logged or passed as a CLI argument. Injected as NMEM_API_KEY env var only.",
|
|
34
|
-
"
|
|
38
|
+
"sensitive": true
|
|
35
39
|
}
|
|
36
40
|
},
|
|
37
41
|
"configSchema": {
|
|
@@ -68,6 +72,13 @@
|
|
|
68
72
|
"maximum": 100,
|
|
69
73
|
"description": "Minimum relevance score (0-100%) to include a memory in auto-recall. Set to 0 to include all results."
|
|
70
74
|
},
|
|
75
|
+
"maxThreadMessageChars": {
|
|
76
|
+
"type": "integer",
|
|
77
|
+
"default": 800,
|
|
78
|
+
"minimum": 200,
|
|
79
|
+
"maximum": 20000,
|
|
80
|
+
"description": "Maximum characters preserved per captured OpenClaw thread message before truncation."
|
|
81
|
+
},
|
|
71
82
|
"apiUrl": {
|
|
72
83
|
"type": "string",
|
|
73
84
|
"default": "",
|
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -20,6 +20,7 @@ const ALLOWED_KEYS = new Set([
|
|
|
20
20
|
"digestMinInterval",
|
|
21
21
|
"maxContextResults",
|
|
22
22
|
"recallMinScore",
|
|
23
|
+
"maxThreadMessageChars",
|
|
23
24
|
"apiUrl",
|
|
24
25
|
"apiKey",
|
|
25
26
|
// Legacy aliases — accepted but not advertised
|
|
@@ -136,7 +137,7 @@ function firstDefined(...options) {
|
|
|
136
137
|
* will configure via OpenClaw's plugin settings UI (pluginConfig).
|
|
137
138
|
*
|
|
138
139
|
* Canonical keys: sessionContext, sessionDigest, digestMinInterval,
|
|
139
|
-
* maxContextResults, recallMinScore, apiUrl, apiKey
|
|
140
|
+
* maxContextResults, recallMinScore, maxThreadMessageChars, apiUrl, apiKey
|
|
140
141
|
*
|
|
141
142
|
* Legacy aliases (accepted from all sources; never shown in docs):
|
|
142
143
|
* autoRecall → sessionContext
|
|
@@ -150,6 +151,7 @@ function firstDefined(...options) {
|
|
|
150
151
|
* NMEM_DIGEST_MIN_INTERVAL — seconds (0-86400)
|
|
151
152
|
* NMEM_MAX_CONTEXT_RESULTS — integer (1-20)
|
|
152
153
|
* NMEM_RECALL_MIN_SCORE — integer (0-100)
|
|
154
|
+
* NMEM_MAX_THREAD_MESSAGE_CHARS — integer (200-20000)
|
|
153
155
|
* NMEM_API_URL — remote server URL
|
|
154
156
|
* NMEM_API_KEY — API key (never logged)
|
|
155
157
|
*/
|
|
@@ -250,6 +252,23 @@ export function parseConfig(raw, logger) {
|
|
|
250
252
|
const recallMinScore = Math.min(100, Math.max(0, Math.trunc(rms.value)));
|
|
251
253
|
_sources.recallMinScore = rms.source;
|
|
252
254
|
|
|
255
|
+
// --- maxThreadMessageChars: file > pluginConfig > env > default ---
|
|
256
|
+
const mtmcEnv = envInt("NMEM_MAX_THREAD_MESSAGE_CHARS");
|
|
257
|
+
const mtmc = firstDefined(
|
|
258
|
+
{ value: pickNum(resolvedFile, "maxThreadMessageChars"), source: "file" },
|
|
259
|
+
{
|
|
260
|
+
value: pickNum(resolvedPlugin, "maxThreadMessageChars"),
|
|
261
|
+
source: "pluginConfig",
|
|
262
|
+
},
|
|
263
|
+
{ value: mtmcEnv, source: "env" },
|
|
264
|
+
{ value: 800, source: "default" },
|
|
265
|
+
);
|
|
266
|
+
const maxThreadMessageChars = Math.min(
|
|
267
|
+
20000,
|
|
268
|
+
Math.max(200, Math.trunc(mtmc.value)),
|
|
269
|
+
);
|
|
270
|
+
_sources.maxThreadMessageChars = mtmc.source;
|
|
271
|
+
|
|
253
272
|
// --- apiUrl: file > pluginConfig > env > "" ---
|
|
254
273
|
const fileUrl =
|
|
255
274
|
typeof resolvedFile.apiUrl === "string" && resolvedFile.apiUrl.trim();
|
|
@@ -284,6 +303,7 @@ export function parseConfig(raw, logger) {
|
|
|
284
303
|
digestMinInterval,
|
|
285
304
|
maxContextResults,
|
|
286
305
|
recallMinScore,
|
|
306
|
+
maxThreadMessageChars,
|
|
287
307
|
apiUrl,
|
|
288
308
|
apiKey,
|
|
289
309
|
_sources,
|
package/src/hooks/capture.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const DEFAULT_MAX_MESSAGE_CHARS = 800;
|
|
5
5
|
const MAX_DISTILL_MESSAGE_CHARS = 2000;
|
|
6
6
|
const MAX_CONVERSATION_CHARS = 30_000;
|
|
7
7
|
const MIN_MESSAGES_FOR_DISTILL = 4;
|
|
@@ -23,7 +23,7 @@ function _setLastCapture(threadId, now) {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function truncate(text, max =
|
|
26
|
+
function truncate(text, max = DEFAULT_MAX_MESSAGE_CHARS) {
|
|
27
27
|
const str = String(text || "").trim();
|
|
28
28
|
if (!str) return "";
|
|
29
29
|
return str.length > max ? `${str.slice(0, max)}…` : str;
|
|
@@ -48,7 +48,7 @@ function extractText(content) {
|
|
|
48
48
|
return parts.join("\n").trim();
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function normalizeRoleMessage(raw) {
|
|
51
|
+
function normalizeRoleMessage(raw, maxMessageChars = DEFAULT_MAX_MESSAGE_CHARS) {
|
|
52
52
|
if (!raw || typeof raw !== "object") return null;
|
|
53
53
|
const msg =
|
|
54
54
|
raw.message && typeof raw.message === "object" ? raw.message : raw;
|
|
@@ -80,7 +80,7 @@ function normalizeRoleMessage(raw) {
|
|
|
80
80
|
|
|
81
81
|
return {
|
|
82
82
|
role,
|
|
83
|
-
content: truncate(text),
|
|
83
|
+
content: truncate(text, maxMessageChars),
|
|
84
84
|
fullContent: text,
|
|
85
85
|
timestamp,
|
|
86
86
|
externalHint,
|
|
@@ -170,7 +170,7 @@ async function resolveHookMessages(event) {
|
|
|
170
170
|
return loadMessagesFromSessionFile(sessionFile);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
async function appendOrCreateThread({ client, logger, event, ctx, reason }) {
|
|
173
|
+
async function appendOrCreateThread({ client, logger, event, ctx, reason, maxMessageChars = DEFAULT_MAX_MESSAGE_CHARS }) {
|
|
174
174
|
const rawMessages = await resolveHookMessages(event);
|
|
175
175
|
if (!Array.isArray(rawMessages) || rawMessages.length === 0) return;
|
|
176
176
|
|
|
@@ -178,7 +178,9 @@ async function appendOrCreateThread({ client, logger, event, ctx, reason }) {
|
|
|
178
178
|
const sessionKey = String(ctx?.sessionKey || ctx?.sessionId || "session");
|
|
179
179
|
const sessionId = String(ctx?.sessionId || "").trim();
|
|
180
180
|
const title = buildThreadTitle(ctx, reason);
|
|
181
|
-
const normalized = rawMessages
|
|
181
|
+
const normalized = rawMessages
|
|
182
|
+
.map((message) => normalizeRoleMessage(message, maxMessageChars))
|
|
183
|
+
.filter(Boolean);
|
|
182
184
|
if (normalized.length === 0) return;
|
|
183
185
|
|
|
184
186
|
const messages = normalized.map((message, index) => ({
|
|
@@ -291,6 +293,7 @@ export function buildAgentEndCaptureHandler(client, cfg, logger) {
|
|
|
291
293
|
event,
|
|
292
294
|
ctx,
|
|
293
295
|
reason: "agent_end",
|
|
296
|
+
maxMessageChars: cfg.maxThreadMessageChars,
|
|
294
297
|
});
|
|
295
298
|
|
|
296
299
|
// 2. Triage + distill: language-agnostic LLM-based capture.
|
|
@@ -376,6 +379,13 @@ export function buildAgentEndCaptureHandler(client, cfg, logger) {
|
|
|
376
379
|
export function buildBeforeResetCaptureHandler(client, _cfg, logger) {
|
|
377
380
|
return async (event, ctx) => {
|
|
378
381
|
const reason = typeof event?.reason === "string" ? event.reason : undefined;
|
|
379
|
-
await appendOrCreateThread({
|
|
382
|
+
await appendOrCreateThread({
|
|
383
|
+
client,
|
|
384
|
+
logger,
|
|
385
|
+
event,
|
|
386
|
+
ctx,
|
|
387
|
+
reason,
|
|
388
|
+
maxMessageChars: _cfg?.maxThreadMessageChars,
|
|
389
|
+
});
|
|
380
390
|
};
|
|
381
391
|
}
|
package/src/tools/status.js
CHANGED
|
@@ -109,6 +109,9 @@ export function createStatusTool(client, _logger, cfg) {
|
|
|
109
109
|
lines.push(
|
|
110
110
|
` recallMinScore: ${cfg.recallMinScore}% (${sources.recallMinScore || "?"})`,
|
|
111
111
|
);
|
|
112
|
+
lines.push(
|
|
113
|
+
` maxThreadMessageChars: ${cfg.maxThreadMessageChars} (${sources.maxThreadMessageChars || "?"})`,
|
|
114
|
+
);
|
|
112
115
|
|
|
113
116
|
details.config = {
|
|
114
117
|
sessionContext: {
|
|
@@ -131,6 +134,10 @@ export function createStatusTool(client, _logger, cfg) {
|
|
|
131
134
|
value: cfg.recallMinScore,
|
|
132
135
|
source: sources.recallMinScore,
|
|
133
136
|
},
|
|
137
|
+
maxThreadMessageChars: {
|
|
138
|
+
value: cfg.maxThreadMessageChars,
|
|
139
|
+
source: sources.maxThreadMessageChars,
|
|
140
|
+
},
|
|
134
141
|
apiUrl: { value: cfg.apiUrl || "(local)", source: sources.apiUrl },
|
|
135
142
|
apiKey: {
|
|
136
143
|
value: cfg.apiKey ? "(set)" : "(not set)",
|