@nowledge/openclaw-nowledge-mem 0.8.35 → 0.8.36
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 +9 -0
- package/CLAUDE.md +10 -4
- package/README.md +15 -9
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/memory-guide/SKILL.md +8 -2
- package/src/client.js +4 -4
- package/src/hooks/behavioral.js +4 -2
- package/src/tools/memory-search.js +3 -0
- package/src/tools/thread-fetch.js +6 -6
- package/src/tools/thread-search.js +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable changes to the Nowledge Mem OpenClaw plugin will be documented in th
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## [0.8.36] - 2026-10-11
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Search first, inspect at most one small targeted thread range per question, and report evidence gaps rather than walking offsets.
|
|
12
|
+
- Preserve explicit target ranges, progressive graph exploration and existing session capture.
|
|
13
|
+
- Default thread reads to eight messages when no limit is supplied.
|
|
14
|
+
- Preserve matched-message positions so late-thread evidence can be selected without page walking.
|
|
15
|
+
|
|
7
16
|
## [0.8.35] - 2026-10-08
|
|
8
17
|
|
|
9
18
|
- Align ClawHub-first code-plugin install metadata with the supported host baseline.
|
package/CLAUDE.md
CHANGED
|
@@ -49,7 +49,7 @@ src/
|
|
|
49
49
|
timeline.js - activity feed: daily grouping, event_type filter, memoryId hints
|
|
50
50
|
forget.js - memory deletion by ID or search
|
|
51
51
|
thread-search.js - search past conversations by keyword
|
|
52
|
-
thread-fetch.js -
|
|
52
|
+
thread-fetch.js - read one small targeted message range (default 8)
|
|
53
53
|
skills/
|
|
54
54
|
memory-guide/
|
|
55
55
|
SKILL.md - agent behavioral skill: when/how to search, save, explore memory (auto-discovered by OpenClaw)
|
|
@@ -139,9 +139,9 @@ When `contextEngine` points elsewhere (or is absent), hooks handle everything. N
|
|
|
139
139
|
- `nowledge_mem_timeline` - activity feed via `nmem f`. Groups by day. `event_type` filter. Exact date range via `date_from`/`date_to` (YYYY-MM-DD). Entries include `(id: <memoryId>)` for chaining to connections.
|
|
140
140
|
- `nowledge_mem_forget` - delete by ID or fuzzy query.
|
|
141
141
|
|
|
142
|
-
### Thread Tools (
|
|
142
|
+
### Thread Tools (bounded conversation retrieval)
|
|
143
143
|
- `nowledge_mem_thread_search` - search past conversations by keyword. Returns threads with matched message snippets, relevance scores, and message counts. Supports `source` filter.
|
|
144
|
-
- `nowledge_mem_thread_fetch` -
|
|
144
|
+
- `nowledge_mem_thread_fetch` - read one small targeted message range from a specific thread. Choose the needed range from search results before the single read; use a known `offset` once and a small `limit` (default 8).
|
|
145
145
|
|
|
146
146
|
### Diagnostics
|
|
147
147
|
- `nowledge_mem_status` - show plugin trust status, memory slot status, effective config (mode, apiUrl, apiKey set, sessionContext, sessionDigest, etc.), backend connectivity, and version. Checks `plugins.allow`: warns if set but plugin not included (loading blocked); shows tip if not set (plugin loads via `entries.enabled` but trust is implicit). Warns if the memory slot points to `memory-core` instead of `openclaw-nowledge-mem` (common after OpenClaw 3.22+ upgrade). No parameters.
|
|
@@ -216,7 +216,7 @@ Legacy aliases accepted silently in all sources for backward compat:
|
|
|
216
216
|
| `nmem --json m delete <id>` | `client.execJson()` in forget.js | Delete |
|
|
217
217
|
| `GET /threads/search?query=&limit=` | `client.searchThreads()` | Thread search enrichment (best-effort, API-only) |
|
|
218
218
|
| `nmem --json t search <q> -n N` | `client.searchThreadsFull()` | Full thread search (CLI-first, API fallback) |
|
|
219
|
-
| `nmem --json t show <id> -n N --offset O` | `client.fetchThread()` |
|
|
219
|
+
| `nmem --json t show <id> -n N --offset O` | `client.fetchThread()` | Read one small targeted message range (default 8) |
|
|
220
220
|
|
|
221
221
|
All commands have API fallback for older CLI versions.
|
|
222
222
|
|
|
@@ -272,3 +272,9 @@ After bumping, commit inside the `community/` submodule, then stage the updated
|
|
|
272
272
|
- Do NOT expose full WM overwrite from agents - section-level patch is the right granularity.
|
|
273
273
|
- Do NOT add cloud dependencies to the core path.
|
|
274
274
|
- Do NOT accept unknown config keys (strict parser in `config.js`).
|
|
275
|
+
|
|
276
|
+
### Bounded thread reads
|
|
277
|
+
|
|
278
|
+
Search memories or threads first, then choose the specific conversation and message range needed. Call `thread_fetch_messages`, a host thread-fetch tool, or `nmem --json t show` at most once per question, using a small message limit (for example 8) and a content limit of 1200 where supported. A known target range may use a nonzero offset once.
|
|
279
|
+
|
|
280
|
+
Never loop over offsets to reconstruct a conversation. Do not raise `--content-limit` to compensate for a slow or incomplete read. Small output limits do not guarantee cheap server-side work on every deployed backend. If a read is slow, incomplete, fails or times out, stop, report the evidence gap, and refine the search rather than retrying pages. A later explicit user request for another range is a new bounded read; a routine “continue” is not permission to drain the thread.
|
package/README.md
CHANGED
|
@@ -196,7 +196,7 @@ The behavioral skill and always-on hook nudge the agent to **search before answe
|
|
|
196
196
|
| "What was I doing last week?" | `nowledge_mem_timeline` | Activity feed grouped by day. Supports exact date ranges. |
|
|
197
197
|
| "How is X connected to Y?" | `nowledge_mem_connections` | Graph walk: edges, entities, EVOLVES chains, document provenance. |
|
|
198
198
|
| Need startup context or today's focus/priorities | `nowledge_mem_context` | Read Context Bundle when available, with Working Memory fallback. Supports Working Memory section-level patch. |
|
|
199
|
-
| Memory has `sourceThreadId` | `nowledge_mem_thread_fetch` |
|
|
199
|
+
| Memory has `sourceThreadId` | `nowledge_mem_thread_fetch` | Read one small targeted source-message range; do not walk offsets. |
|
|
200
200
|
| "Find our discussion about X" | `nowledge_mem_thread_search` | Search past conversations by keyword. Returns matched snippets. |
|
|
201
201
|
| "Forget X" | `nowledge_mem_forget` | Delete by ID or search query. |
|
|
202
202
|
| "Is my setup working?" | `nowledge_mem_status` | Show effective config, backend connectivity, and version. |
|
|
@@ -230,17 +230,17 @@ flowchart TD
|
|
|
230
230
|
- Distilled memories carry `sourceThreadId`, linking them back to the source conversation
|
|
231
231
|
- Cooldown (`digestMinInterval`, default 300s) prevents burst distillation
|
|
232
232
|
|
|
233
|
-
###
|
|
233
|
+
### Targeted Retrieval (Memory -> Thread -> Messages)
|
|
234
234
|
|
|
235
235
|
Memories distilled from conversations carry a `sourceThreadId`. This creates a retrieval chain:
|
|
236
236
|
|
|
237
237
|
```mermaid
|
|
238
238
|
flowchart TD
|
|
239
239
|
A["memory_search'PostgreSQL decision'"] --> B["Result includessourceThreadId"]
|
|
240
|
-
B --> C["
|
|
241
|
-
C --> D["
|
|
242
|
-
D --> E["
|
|
243
|
-
E --> F["
|
|
240
|
+
B --> C["Choose one known target message range"]
|
|
241
|
+
C --> D["nowledge_mem_thread_fetch(offset=target, limit=8) once"]
|
|
242
|
+
D --> E["Answer from evidence or report the gap"]
|
|
243
|
+
E --> F["Refine search if specific evidence is still missing"]
|
|
244
244
|
```
|
|
245
245
|
|
|
246
246
|
Direct conversation search also works:
|
|
@@ -365,15 +365,15 @@ last_n_days: 7
|
|
|
365
365
|
|
|
366
366
|
**nowledge_mem_thread_search** - Search past conversations by keyword. Returns matched threads with message snippets and relevance scores. Use when the user asks about a past discussion or wants to find a conversation from a specific time.
|
|
367
367
|
|
|
368
|
-
**nowledge_mem_thread_fetch** -
|
|
368
|
+
**nowledge_mem_thread_fetch** - Read one small targeted message range. Pass a `sourceThreadId` from memory results or a `threadId` from thread search. Supports `offset` and `limit` (default 8); a known nonzero offset is valid once, not a reason to walk pages.
|
|
369
369
|
|
|
370
370
|
```
|
|
371
371
|
threadId: "openclaw-db-arch-a1b2c3"
|
|
372
|
-
offset: 0, limit:
|
|
372
|
+
offset: 0, limit: 8
|
|
373
373
|
-> Thread: "Database architecture discussion" (128 messages)
|
|
374
374
|
[user] We need to decide on the database for task events...
|
|
375
375
|
[assistant] Based on the requirements, PostgreSQL with JSONB...
|
|
376
|
-
... (126
|
|
376
|
+
... (126 messages not shown; refine search for the specific evidence needed)
|
|
377
377
|
```
|
|
378
378
|
|
|
379
379
|
## Operating Modes
|
|
@@ -644,3 +644,9 @@ Run `nmem config client show` and confirm the effective URL and API key state. T
|
|
|
644
644
|
## License
|
|
645
645
|
|
|
646
646
|
MIT
|
|
647
|
+
|
|
648
|
+
### Bounded thread reads
|
|
649
|
+
|
|
650
|
+
Search memories or threads first, then choose the specific conversation and message range needed. Call `thread_fetch_messages`, a host thread-fetch tool, or `nmem --json t show` at most once per question, using a small message limit (for example 8) and a content limit of 1200 where supported. A known target range may use a nonzero offset once.
|
|
651
|
+
|
|
652
|
+
Never loop over offsets to reconstruct a conversation. Do not raise `--content-limit` to compensate for a slow or incomplete read. Small output limits do not guarantee cheap server-side work on every deployed backend. If a read is slow, incomplete, fails or times out, stop, report the evidence gap, and refine the search rather than retrying pages. A later explicit user request for another range is a new bounded read; a routine “continue” is not permission to drain the thread.
|
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 startup context, graph search, and session distillation.",
|
|
5
|
-
"version": "0.8.
|
|
5
|
+
"version": "0.8.36",
|
|
6
6
|
"kind": ["memory", "context-engine"],
|
|
7
7
|
"contracts": {
|
|
8
8
|
"tools": [
|
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.36",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Nowledge Mem memory plugin for OpenClaw, local-first personal knowledge base",
|
|
7
7
|
"author": {
|
|
@@ -22,7 +22,7 @@ Use natural language queries. This is semantic search, not file search.
|
|
|
22
22
|
|
|
23
23
|
Results include `matchedVia` (scoring breakdown), `importance`, `labels`, `sourceThreadId`.
|
|
24
24
|
|
|
25
|
-
When results include `relatedThreads`, use `nowledge_mem_thread_fetch` with the `threadId` for
|
|
25
|
+
When results include `relatedThreads`, use `nowledge_mem_thread_fetch` with the `threadId` for one small targeted message range. A known offset may select that range; do not advance through pages. Follow the bounded thread-read policy below.
|
|
26
26
|
|
|
27
27
|
Bi-temporal filters narrow results by time:
|
|
28
28
|
|
|
@@ -52,7 +52,7 @@ Browse recent activity grouped by day. Filter by `event_type` or exact `date_fro
|
|
|
52
52
|
|
|
53
53
|
## Threads (nowledge_mem_thread_search + nowledge_mem_thread_fetch)
|
|
54
54
|
|
|
55
|
-
Find past conversations by keyword, then
|
|
55
|
+
Find past conversations by keyword, then once for the specific messages needed fetch messages. Threads span all sources: this tool, other AI tools, browser capture, imports.
|
|
56
56
|
|
|
57
57
|
## Startup Context (nowledge_mem_context)
|
|
58
58
|
|
|
@@ -63,3 +63,9 @@ Read Context Bundle when identity, active space, active rules, or current priori
|
|
|
63
63
|
- Search uses natural language queries, not file paths. Never search for "MEMORY.md" or "memory/*.md".
|
|
64
64
|
- When `relatedThreads` appear in results, they often contain the most useful context.
|
|
65
65
|
- Use tools directly; do not tell the user to "check your memory."
|
|
66
|
+
|
|
67
|
+
### Bounded thread reads
|
|
68
|
+
|
|
69
|
+
Search memories or threads first, then choose the specific conversation and message range needed. Call `thread_fetch_messages`, a host thread-fetch tool, or `nmem --json t show` at most once per question, using a small message limit (for example 8) and a content limit of 1200 where supported. A known target range may use a nonzero offset once.
|
|
70
|
+
|
|
71
|
+
Never loop over offsets to reconstruct a conversation. Do not raise `--content-limit` to compensate for a slow or incomplete read. Small output limits do not guarantee cheap server-side work on every deployed backend. If a read is slow, incomplete, fails or times out, stop, report the evidence gap, and refine the search rather than retrying pages. A later explicit user request for another range is a new bounded read; a routine “continue” is not permission to drain the thread.
|
package/src/client.js
CHANGED
|
@@ -968,8 +968,8 @@ export class NowledgeMemClient {
|
|
|
968
968
|
}
|
|
969
969
|
|
|
970
970
|
/**
|
|
971
|
-
*
|
|
972
|
-
*
|
|
971
|
+
* Read one small targeted message range from a specific thread.
|
|
972
|
+
* A known target offset is supported; do not walk offsets to reconstruct a conversation.
|
|
973
973
|
*
|
|
974
974
|
* Uses CLI: `nmem --json t show <id> -n N --offset O`
|
|
975
975
|
* Falls back to API: GET /threads/{id}
|
|
@@ -977,13 +977,13 @@ export class NowledgeMemClient {
|
|
|
977
977
|
* @param {string} threadId Thread ID
|
|
978
978
|
* @param {{ offset?: number; limit?: number }} [options]
|
|
979
979
|
*/
|
|
980
|
-
async fetchThread(threadId, { offset = 0, limit =
|
|
980
|
+
async fetchThread(threadId, { offset = 0, limit = 8 } = {}) {
|
|
981
981
|
const id = String(threadId || "").trim();
|
|
982
982
|
if (!id) throw new Error("fetchThread requires threadId");
|
|
983
983
|
|
|
984
984
|
const normalizedLimit = Math.min(
|
|
985
985
|
200,
|
|
986
|
-
Math.max(1, Math.trunc(Number(limit) ||
|
|
986
|
+
Math.max(1, Math.trunc(Number(limit) || 8)),
|
|
987
987
|
);
|
|
988
988
|
const normalizedOffset = Math.max(0, Math.trunc(Number(offset) || 0));
|
|
989
989
|
|
package/src/hooks/behavioral.js
CHANGED
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
|
|
18
18
|
import { ceState } from "../ce-state.js";
|
|
19
19
|
|
|
20
|
+
const THREAD_READ_GUIDANCE = "When results include a sourceThreadId, use nowledge_mem_thread_fetch for at most one small targeted message range per question (for example 8 messages). Never loop over offsets or increase content limits. If the read is slow, incomplete or fails, stop, report the evidence gap and refine search.";
|
|
21
|
+
|
|
20
22
|
export const BASE_GUIDANCE = [
|
|
21
23
|
"<nowledge-mem-guidance>",
|
|
22
24
|
"You have access to the user's personal knowledge graph (Nowledge Mem).",
|
|
23
25
|
"Search proactively with memory_search when past context would improve your answer. Do not wait to be asked. Use natural language queries, not file paths.",
|
|
24
|
-
|
|
26
|
+
THREAD_READ_GUIDANCE,
|
|
25
27
|
"Save autonomously with nowledge_mem_save when the conversation produces a decision, preference, plan, procedure, or learning. Do not wait to be asked.",
|
|
26
28
|
"</nowledge-mem-guidance>",
|
|
27
29
|
].join("\n");
|
|
@@ -31,7 +33,7 @@ export const SESSION_CONTEXT_GUIDANCE = [
|
|
|
31
33
|
"You have access to the user's personal knowledge graph (Nowledge Mem).",
|
|
32
34
|
"Relevant memories and your Working Memory have already been injected into this prompt.",
|
|
33
35
|
"Use memory_search for specifics beyond what was auto-recalled, or when the user references prior work not covered above. Natural language queries, not file paths.",
|
|
34
|
-
|
|
36
|
+
THREAD_READ_GUIDANCE,
|
|
35
37
|
"Save autonomously with nowledge_mem_save when the conversation produces a decision, preference, plan, procedure, or learning. Do not wait to be asked.",
|
|
36
38
|
"</nowledge-mem-guidance>",
|
|
37
39
|
].join("\n");
|
|
@@ -20,6 +20,7 @@ export function createMemorySearchTool(client, logger) {
|
|
|
20
20
|
"Uses multi-signal scoring: semantic, BM25 keyword, label, graph & community signals, recency/importance decay. " +
|
|
21
21
|
"Returns snippets with memoryIds — pass a memoryId to nowledge_mem_connections for cross-topic synthesis or source provenance. " +
|
|
22
22
|
"Also returns relevant past conversation snippets (relatedThreads) when available. " +
|
|
23
|
+
"Use a matched messageIndex, when present, to choose one small targeted thread range; never walk offsets or invent missing positions. " +
|
|
23
24
|
"Supports bi-temporal filtering: event_date_from/to (when the fact HAPPENED), recorded_date_from/to (when it was SAVED). " +
|
|
24
25
|
"For browsing recent activity by day use nowledge_mem_timeline instead.",
|
|
25
26
|
parameters: {
|
|
@@ -163,6 +164,8 @@ export function createMemorySearchTool(client, logger) {
|
|
|
163
164
|
matchedMessages: msgs.map((m) => ({
|
|
164
165
|
role: m.role,
|
|
165
166
|
snippet: truncateSnippet(m.snippet, 200),
|
|
167
|
+
...(Number.isInteger(m.message_index) && m.message_index >= 0
|
|
168
|
+
? { messageIndex: m.message_index } : {}),
|
|
166
169
|
})),
|
|
167
170
|
};
|
|
168
171
|
});
|
|
@@ -2,11 +2,11 @@ export function createThreadFetchTool(client, logger) {
|
|
|
2
2
|
return {
|
|
3
3
|
name: "nowledge_mem_thread_fetch",
|
|
4
4
|
description:
|
|
5
|
-
"Fetch
|
|
5
|
+
"Fetch a targeted range of messages from a conversation thread. Use to read context " +
|
|
6
6
|
"around a memory — pass the sourceThreadId from memory_search or memory_get results, " +
|
|
7
7
|
"or a threadId from nowledge_mem_thread_search. " +
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"Use a known target offset to skip earlier messages once. " +
|
|
9
|
+
"Search first, then fetch at most one small targeted message range per question. Never loop over offsets to reconstruct a conversation or increase content limits. If the read is slow, incomplete or fails, stop, report the evidence gap and refine search.",
|
|
10
10
|
parameters: {
|
|
11
11
|
type: "object",
|
|
12
12
|
properties: {
|
|
@@ -17,11 +17,11 @@ export function createThreadFetchTool(client, logger) {
|
|
|
17
17
|
},
|
|
18
18
|
offset: {
|
|
19
19
|
type: "number",
|
|
20
|
-
description: "
|
|
20
|
+
description: "Known target message offset (default 0); never walk successive pages",
|
|
21
21
|
},
|
|
22
22
|
limit: {
|
|
23
23
|
type: "number",
|
|
24
|
-
description: "Max messages to return (1-200, default
|
|
24
|
+
description: "Max messages to return (1-200, default 8)",
|
|
25
25
|
},
|
|
26
26
|
},
|
|
27
27
|
required: ["threadId"],
|
|
@@ -35,7 +35,7 @@ export function createThreadFetchTool(client, logger) {
|
|
|
35
35
|
);
|
|
36
36
|
const limit = Math.min(
|
|
37
37
|
200,
|
|
38
|
-
Math.max(1, Math.trunc(Number(safeParams.limit ??
|
|
38
|
+
Math.max(1, Math.trunc(Number(safeParams.limit ?? 8) || 8)),
|
|
39
39
|
);
|
|
40
40
|
|
|
41
41
|
if (!threadId) {
|
|
@@ -10,10 +10,12 @@ export function createThreadSearchTool(client, logger) {
|
|
|
10
10
|
name: "nowledge_mem_thread_search",
|
|
11
11
|
description:
|
|
12
12
|
"Search past conversations by keyword. Use when the user asks about a past discussion, " +
|
|
13
|
-
"wants to find a conversation from a specific time, or when you want to
|
|
13
|
+
"wants to find a conversation from a specific time, or when you want to find relevant source messages " +
|
|
14
14
|
"around a topic beyond what memory_search returned. " +
|
|
15
15
|
"Returns matched threads with message snippets and relevance scores. " +
|
|
16
|
-
"
|
|
16
|
+
"A match's messageIndex, when present, identifies a target offset for a nearby range; do not invent positions when absent. " +
|
|
17
|
+
"Pass its threadId to nowledge_mem_thread_fetch for at most one small targeted message range per question. " +
|
|
18
|
+
"Never loop over offsets; on slow, incomplete or failed reads, report the evidence gap and refine search.",
|
|
17
19
|
parameters: {
|
|
18
20
|
type: "object",
|
|
19
21
|
properties: {
|
|
@@ -74,6 +76,8 @@ export function createThreadSearchTool(client, logger) {
|
|
|
74
76
|
matchedMessages: msgs.map((m) => ({
|
|
75
77
|
role: m.role,
|
|
76
78
|
snippet: truncateSnippet(m.snippet),
|
|
79
|
+
...(Number.isInteger(m.message_index) && m.message_index >= 0
|
|
80
|
+
? { messageIndex: m.message_index } : {}),
|
|
77
81
|
})),
|
|
78
82
|
};
|
|
79
83
|
});
|