Skip to content

bump v0.6.1#71

Merged
wey-gu merged 9 commits into
mainfrom
dev_061
Feb 24, 2026
Merged

bump v0.6.1#71
wey-gu merged 9 commits into
mainfrom
dev_061

Conversation

@wey-gu

@wey-gu wey-gu commented Feb 23, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

Release Notes v0.3.0

  • New Features

    • Tool-only mode is now the default with zero overhead; LLM-based memory capture pipeline with triage and distillation stages replaces heuristic-based capture.
  • Configuration Changes

    • Auto-recall now defaults to off; memory queries are on-demand via tools instead of automatic at session start.
  • Documentation

    • Updated README and UI configuration to reflect new operating modes and LLM-powered memory workflow.

wey-gu and others added 3 commits February 23, 2026 18:20
Breaking: autoRecall now defaults to false. The agent has full access to
all 7 tools regardless of this setting and calls them on demand — no tokens
wasted on irrelevant context injection per turn.

Removed: English-only heuristic capture pipeline
- shouldCaptureAsMemory() with regex patterns like /\bi (like|prefer|hate)\b/i
- MEMORY_TRIGGER_PATTERNS, PROMPT_INJECTION_PATTERNS
- looksLikeQuestion(), hasMemoryTrigger(), looksLikePromptInjection()
- fingerprint(), per-session dedup map
These were broken for ~95% of the world (non-English users).

Added: Two-step LLM capture pipeline (autoCapture mode)
1. Thread capture — unconditional, idempotent (unchanged)
2. Triage — cheap LLM call (~50 output tokens) via POST /memories/distill/triage
3. Distillation — full extraction via POST /memories/distill, only when triage
   says worthwhile. Creates typed memories with unit_type, labels, temporal data.
Language-agnostic. Works in any language.

Also:
- Enhanced memory_search description: directive guidance for proactive search
- Migrated autoRecall hook from before_agent_start to before_prompt_build
- Trimmed recall context injection (agent already sees tool descriptions)
- client.triageConversation() + client.distillThread() new API methods

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auto-formatted pre-existing files to satisfy biome check.
No logic changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
node_modules/ was committed in the previous chore commit after
running npm install for biome. Adding .gitignore and removing
from tracking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Feb 23, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR upgrades the plugin to version 0.3.0, introducing an LLM-based triage and distillation pipeline for memory capture, replacing legacy heuristic-based methods. Default behavior changes with autoRecall now false, new client methods are added (triageConversation and distillThread), and tool descriptions are refined to reflect on-demand memory querying.

Changes

Cohort / File(s) Summary
Version & Package
package.json, openclaw.plugin.json, CHANGELOG.md
Version bumped to 0.3.0; UI hints and config schema updated to reflect autoRecall default change (true → false), enhanced descriptions for autoCapture and autoRecall behaviors, and documented major architecture shift to LLM-based capture.
Documentation
README.md
Restructured sections from narrative to tabular mode-based presentation; Operating Modes table introduced with Tool-only (default), Auto-recall, and Auto-capture; removed older enablement defaults and updated behavior descriptions.
Configuration & Initialization
src/config.js, src/index.js, src/client.js
Added API\_DEFAULT\_URL constant and isDefaultApiUrl helper; stricter unknown-key validation in parseConfig; autoRecall default changed to false; two new public client methods: triageConversation(content) and distillThread({ threadId, title, content }).
Memory Hooks
src/hooks/capture.js, src/hooks/recall.js
Removed legacy heuristics (fingerprint, language-specific patterns); replaced memory-note logic with LLM-driven triage/distill pipeline; appendOrCreateThread now returns structured objects; buildAgentEndCaptureHandler orchestrates conditional distillation; recall hook simplified with minimal tool guidance.
Tool Implementations
src/tools/memory-search.js, src/tools/save.js, src/tools/connections.js, src/tools/context.js
memory\_search description rewritten for concise, usage-oriented guidance; connections tool adds id line to output; context and save tools refined with formatting adjustments; no logic changes to core functionality.
Git Configuration
.gitignore
Added node\_modules/ directory to ignore list.

Sequence Diagram(s)

sequenceDiagram
    participant Agent as Agent/Handler
    participant Client as NowledgeMemClient
    participant TriageAPI as Backend<br/>(Triage Endpoint)
    participant DistillAPI as Backend<br/>(Distill Endpoint)
    participant Memory as Memory Storage

    Agent->>Agent: Session ends
    Agent->>Memory: Append/create thread with conversation
    Memory-->>Agent: threadId, normalized content
    Agent->>Client: triageConversation(content)
    Client->>TriageAPI: POST /memories/distill/triage<br/>(thread_content)
    TriageAPI-->>Client: { approved: boolean }
    
    alt If approved
        Agent->>Client: distillThread({ threadId, title, content })
        Client->>DistillAPI: POST /memories/distill<br/>(thread metadata + content)<br/>timeout: 60s
        DistillAPI-->>Client: { distilled result }
        Client-->>Agent: Log distillation result
    else If not approved
        Client-->>Agent: Skip distillation, log decision
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • V0.6 update #50 — Overlaps on the same plugin codebase with changes to client, config, hooks, and tool implementations across multiple files.

Poem

🐰 Heuristics fade, LLM takes the stage,
Old patterns gone, a language-agnostic page,
Triage and distill in elegant pipeline flow,
No English-only chains, let wisdom grow!
Tool-first, on-demand, the future's in sight—
Memory reborn, brilliantly bright.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'bump v0.6.1' is misleading and unrelated to the actual changeset. Version numbers in package.json and openclaw.plugin.json are bumped to 0.3.0, not 0.6.1. Update the PR title to accurately reflect the main change, such as 'bump version to 0.3.0 and refactor capture pipeline to LLM-based triage/distill' or a similarly specific description of the architecture changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 47.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev_061

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@wey-gu wey-gu changed the title bump v0.6 bump v0.6.1 Feb 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
nowledge-mem-openclaw-plugin/src/tools/save.js (1)

32-34: ⚠️ Potential issue | 🟡 Minor

context unit type is missing from the top-level tool description.

VALID_UNIT_TYPES contains 8 entries including context, and the unit_type property's own description (Lines 51–53) lists it correctly. But the top-level description string on Lines 32–34 enumerates only 7 types and omits context. An LLM reading only the tool-level description may never consider emitting context as unit_type, degrading graph richness.

📝 Proposed fix
- "Specify unit_type to give the memory richer structure: " +
- "decision (a choice made), learning (an insight gained), preference (user taste), " +
- "fact (verified info), plan (future intent), procedure (how-to steps), event (something that happened). " +
+ "Specify unit_type to give the memory richer structure: " +
+ "decision (a choice made), learning (an insight gained), preference (user taste), " +
+ "fact (verified info), plan (future intent), procedure (how-to steps), " +
+ "context (background info), event (something that happened). " +
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/tools/save.js` around lines 32 - 34, The
top-level tool description string in save.js lists unit types but omits
"context", causing inconsistency with VALID_UNIT_TYPES and the unit_type
property; update that description (the string concatenated across the lines
around the phrase "decision (a choice made) ... event_start for when the event
HAPPENED") to include "context (background or surrounding information)" among
the enumerated types so it matches VALID_UNIT_TYPES and the unit_type property's
documentation.
nowledge-mem-openclaw-plugin/src/tools/connections.js (2)

203-210: ⚠️ Potential issue | 🟡 Minor

EVOLVES and SOURCED_FROM neighbor sections omit strength %.

CRYSTALLIZED_FROM renders [${(weight * 100).toFixed(0)}%] from the weight field, but EVOLVES (lines 203–210) and SOURCED_FROM (lines 217–225) both destructure the weight from the connection object but never render it.

🛠 Proposed fix for EVOLVES neighbor section
-	const lines = evolvesConns.map(({ node, relation }) => {
+	const lines = evolvesConns.map(({ node, relation, weight }) => {
 		const title = getNodeTitle(node);
 		const relLabel =
 			EVOLVES_RELATION_LABELS[relation] ||
 			relation ||
 			"related knowledge";
-		return `  - ${relLabel}\n    "${title}"\n    → id: ${node.id}`;
+		const strength = weight ? ` [${(weight * 100).toFixed(0)}%]` : "";
+		return `  - ${relLabel}${strength}\n    "${title}"\n    → id: ${node.id}`;
 	});
🛠 Proposed fix for SOURCED_FROM section
-	const lines = sourceConns.map(({ node }) => {
+	const lines = sourceConns.map(({ node, weight }) => {
 		const name = getNodeTitle(node);
 		const sourceType =
 			node.metadata?.source_type || node.source_type || "document";
-		return `  - ${name} (${sourceType}) → id: ${node.id}`;
+		const strength = weight ? ` [${(weight * 100).toFixed(0)}%]` : "";
+		return `  - ${name} (${sourceType})${strength} → id: ${node.id}`;
 	});

Based on learnings: "Tool nowledge_mem_connections must support graph exploration with edges: … showing strength % and memoryId."

Also applies to: 217-225

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/tools/connections.js` around lines 203 -
210, The EVOLVES neighbor rendering (in the evolvesConns map that uses
getNodeTitle and EVOLVES_RELATION_LABELS) and the SOURCED_FROM section both
destructure weight but never render it; update the template strings in the
evolvesConns mapping and the sourced-from connection mapping to include the
strength percentage like `[${(weight * 100).toFixed(0)}%]` and also include the
node/memory id (e.g., `id: ${node.id}` or `memoryId`) so each edge shows
strength % and memoryId; ensure you reference the same variables used in each
map (weight, node, relation) and keep the existing relLabel/title formatting.

231-236: ⚠️ Potential issue | 🟡 Minor

MENTIONS connections are missing both memoryId and strength % — guideline violation.

The guideline requires all connection sections to show "strength % and memoryId". Every other section emits → id: ${node.id} and at least attempts a strength rendering, but the MENTIONS block includes neither.

🛠 Proposed fix
 const lines = mentionConns.map(({ node }) => {
-	const name = getNodeTitle(node);
-	const type = getNodeType(node);
-	return `  - ${name} (${type})`;
+	const name = getNodeTitle(node);
+	const type = getNodeType(node);
+	return `  - ${name} (${type}) → id: ${node.id}`;
 });

Based on learnings: "Tool nowledge_mem_connections must support graph exploration with edges: CRYSTALLIZED_FROM, EVOLVES (with sub-relations: supersedes/enriches/confirms/challenges), SOURCED_FROM, MENTIONS, showing strength % and memoryId."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/tools/connections.js` around lines 231 -
236, The MENTIONS block omits memoryId and strength because mentionConns is
mapped only for { node }; update the mapping in the mentionConns handling (where
mentionConns.map is used) to destructure the full connection object (e.g., {
node, memoryId, strength } or the actual property names used elsewhere) and
include the same strength rendering and node id shown in other sections—use the
existing strength formatter function (e.g., renderStrength or formatStrength)
and append "→ id: ${node.id} strength: ${...}% memoryId: ${memoryId}" to each
line so the lines pushed to sections match other connection blocks.
🧹 Nitpick comments (3)
nowledge-mem-openclaw-plugin/openclaw.plugin.json (1)

16-16: Consider mentioning the autoRecall dependency in the UI hint for maxRecallResults.

The configSchema description (line 46) correctly notes "only used when autoRecall is enabled", but the uiHints.help (line 16) omits this, which could confuse users who see the setting in the UI without understanding why changing it has no effect.

✏️ Suggested wording update
-			"help": "How many memories to inject for each recall cycle (1\u201320)"
+			"help": "How many memories to inject per turn when Auto-recall is on (1\u201320)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/openclaw.plugin.json` at line 16, Update the
uiHints.help for the maxRecallResults setting to state that it only applies when
autoRecall is enabled; specifically, modify the "help" string (referenced by
uiHints.help for maxRecallResults) to include a note such as "Only used when
autoRecall is enabled" so it matches the configSchema description and prevents
user confusion.
nowledge-mem-openclaw-plugin/src/hooks/capture.js (1)

154-218: Inconsistent return sentinels from appendOrCreateThread.

Early-exit paths (no messages / empty normalized list) return undefined while error paths return null. The caller handles both correctly via !result, but the inconsistency makes the contract harder to reason about.

♻️ Suggested fix — unify all non-success paths to return null
-  if (!Array.isArray(rawMessages) || rawMessages.length === 0) return;
+  if (!Array.isArray(rawMessages) || rawMessages.length === 0) return null;
   ...
-  if (normalized.length === 0) return;
+  if (normalized.length === 0) return null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js` around lines 154 - 218,
The function appendOrCreateThread has mixed non-success returns (undefined on
early exits and null on errors); make all non-success paths consistently return
null: change the early-exit returns after resolveHookMessages and after
filtering normalized to return null instead of undefined so every non-success
path (no messages, empty normalized, or failed append/create) returns null while
successful paths continue to return { threadId, normalized } and logs; update
the return statements in appendOrCreateThread accordingly.
nowledge-mem-openclaw-plugin/src/client.js (1)

697-723: title field in distillThread is not coerced to String.

All other fields in the same payload (thread_id, thread_content) use String(x || ""). thread_title: title || "" skips the String() coercion. While title is typed as string? in the JSDoc, the pattern is inconsistent.

♻️ Suggested fix
-            thread_title: title || "",
+            thread_title: String(title || ""),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/client.js` around lines 697 - 723, The
distillThread payload inconsistently sets thread_title using title || "" instead
of coercing to a string like the other fields; change the payload in
distillThread so thread_title is assigned String(title || "") to match thread_id
and thread_content (update the distillThread method where thread_title is set).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nowledge-mem-openclaw-plugin/CHANGELOG.md`:
- Around line 25-31: The header "Two-step LLM capture pipeline" is inconsistent
with the three-item list ("Thread capture", "Triage", "Distillation"); update
the text to make intent explicit by either renaming the header to "Three-step
capture pipeline" or rewording it to "Two-step LLM capture pipeline (plus
unconditional thread capture)" and keep the numbered items as-is; ensure the
existing item labels "Thread capture", "Triage", "Distillation" and the endpoint
mention `POST /memories/distill/triage` remain unchanged for clarity.

In `@nowledge-mem-openclaw-plugin/README.md`:
- Around line 121-141: Update the README to clarify that the LLM distillation
step runs only at the agent_end hook and not for the other two hooks;
specifically state that buildBeforeResetCaptureHandler is thread-only (no
distillation) and that after_compaction likewise only triggers thread capture
unless invoked from agent_end, while agent_end triggers both thread capture and
the LLM distillation pass. Mention the exact symbols: agent_end,
after_compaction, buildBeforeResetCaptureHandler so readers can map behavior to
implementation.

In `@nowledge-mem-openclaw-plugin/src/tools/connections.js`:
- Around line 287-297: The EVOLVES chain rendering omits the neighbor node's
memoryId; update the edges.map callback (the block using
EVOLVES_RELATION_LABELS, targetId, otherTitle, direction) to compute the
neighbor id as const otherId = isOlderNode ? edge.newer_id : edge.older_id and
include that otherId in the returned line string (e.g., append " (memoryId:
<otherId>)" after otherTitle) so each connection shows the neighboring node's
memoryId.

---

Outside diff comments:
In `@nowledge-mem-openclaw-plugin/src/tools/connections.js`:
- Around line 203-210: The EVOLVES neighbor rendering (in the evolvesConns map
that uses getNodeTitle and EVOLVES_RELATION_LABELS) and the SOURCED_FROM section
both destructure weight but never render it; update the template strings in the
evolvesConns mapping and the sourced-from connection mapping to include the
strength percentage like `[${(weight * 100).toFixed(0)}%]` and also include the
node/memory id (e.g., `id: ${node.id}` or `memoryId`) so each edge shows
strength % and memoryId; ensure you reference the same variables used in each
map (weight, node, relation) and keep the existing relLabel/title formatting.
- Around line 231-236: The MENTIONS block omits memoryId and strength because
mentionConns is mapped only for { node }; update the mapping in the mentionConns
handling (where mentionConns.map is used) to destructure the full connection
object (e.g., { node, memoryId, strength } or the actual property names used
elsewhere) and include the same strength rendering and node id shown in other
sections—use the existing strength formatter function (e.g., renderStrength or
formatStrength) and append "→ id: ${node.id} strength: ${...}% memoryId:
${memoryId}" to each line so the lines pushed to sections match other connection
blocks.

In `@nowledge-mem-openclaw-plugin/src/tools/save.js`:
- Around line 32-34: The top-level tool description string in save.js lists unit
types but omits "context", causing inconsistency with VALID_UNIT_TYPES and the
unit_type property; update that description (the string concatenated across the
lines around the phrase "decision (a choice made) ... event_start for when the
event HAPPENED") to include "context (background or surrounding information)"
among the enumerated types so it matches VALID_UNIT_TYPES and the unit_type
property's documentation.

---

Nitpick comments:
In `@nowledge-mem-openclaw-plugin/openclaw.plugin.json`:
- Line 16: Update the uiHints.help for the maxRecallResults setting to state
that it only applies when autoRecall is enabled; specifically, modify the "help"
string (referenced by uiHints.help for maxRecallResults) to include a note such
as "Only used when autoRecall is enabled" so it matches the configSchema
description and prevents user confusion.

In `@nowledge-mem-openclaw-plugin/src/client.js`:
- Around line 697-723: The distillThread payload inconsistently sets
thread_title using title || "" instead of coercing to a string like the other
fields; change the payload in distillThread so thread_title is assigned
String(title || "") to match thread_id and thread_content (update the
distillThread method where thread_title is set).

In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js`:
- Around line 154-218: The function appendOrCreateThread has mixed non-success
returns (undefined on early exits and null on errors); make all non-success
paths consistently return null: change the early-exit returns after
resolveHookMessages and after filtering normalized to return null instead of
undefined so every non-success path (no messages, empty normalized, or failed
append/create) returns null while successful paths continue to return {
threadId, normalized } and logs; update the return statements in
appendOrCreateThread accordingly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ecb996 and 83712f0.

⛔ Files ignored due to path filters (1)
  • nowledge-mem-openclaw-plugin/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • nowledge-mem-openclaw-plugin/.gitignore
  • nowledge-mem-openclaw-plugin/CHANGELOG.md
  • nowledge-mem-openclaw-plugin/README.md
  • nowledge-mem-openclaw-plugin/openclaw.plugin.json
  • nowledge-mem-openclaw-plugin/package.json
  • nowledge-mem-openclaw-plugin/src/client.js
  • nowledge-mem-openclaw-plugin/src/config.js
  • nowledge-mem-openclaw-plugin/src/hooks/capture.js
  • nowledge-mem-openclaw-plugin/src/hooks/recall.js
  • nowledge-mem-openclaw-plugin/src/index.js
  • nowledge-mem-openclaw-plugin/src/tools/connections.js
  • nowledge-mem-openclaw-plugin/src/tools/context.js
  • nowledge-mem-openclaw-plugin/src/tools/memory-search.js
  • nowledge-mem-openclaw-plugin/src/tools/save.js

Comment thread nowledge-mem-openclaw-plugin/CHANGELOG.md
Comment thread nowledge-mem-openclaw-plugin/README.md
Comment thread nowledge-mem-openclaw-plugin/src/tools/connections.js Outdated
…emoryId

1. capture.js: clarify in JSDoc that LLM triage+distill runs exclusively
   in the agent_end handler; before_reset/after_compaction are thread-only.
2. connections.js: EVOLVES chain output now includes the neighboring
   node's memoryId (→ id: ...) for follow-up graph exploration, matching
   the pattern already used by CRYSTALLIZED_FROM and other sections.

Addresses codexstellaris review comments on community PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
nowledge-mem-openclaw-plugin/src/hooks/capture.js (1)

154-217: Inconsistent implicit vs. explicit null returns in early-exit paths

Lines 156 and 163 return undefined (bare return;), while the error paths at lines 198 and 216 explicitly return null. The caller handles both correctly via !result, but normalising to a single sentinel would make the contract clearer.

♻️ Proposed fix
-	if (!Array.isArray(rawMessages) || rawMessages.length === 0) return;
+	if (!Array.isArray(rawMessages) || rawMessages.length === 0) return null;

 	// ...

-	if (normalized.length === 0) return;
+	if (normalized.length === 0) return null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js` around lines 154 - 217,
The function appendOrCreateThread mixes implicit undefined returns (bare return)
and explicit null returns; change the early-exit returns after the rawMessages
check and after normalized.length === 0 to return null so all failure/early-exit
paths consistently return null (preserving existing success returns like {
threadId, normalized }); update any local comments or JSDoc for
appendOrCreateThread if present to reflect the unified null sentinel.
nowledge-mem-openclaw-plugin/src/tools/connections.js (2)

228-237: MENTIONS connections omit both node.id and strength — guideline compliance gap.

Every other edge type in this tool (CRYSTALLIZED_FROM, SOURCED_FROM, RELATED) shows the connected node's ID and/or strength. MENTIONS shows neither, even though both weight and node.id are available.

♻️ Proposed fix
- const lines = mentionConns.map(({ node }) => {
+ const lines = mentionConns.map(({ node, weight }) => {
      const name = getNodeTitle(node);
      const type = getNodeType(node);
-     return `  - ${name} (${type})`;
+     const strength = weight ? ` [${(weight * 100).toFixed(0)}%]` : "";
+     return `  - ${name} (${type})${strength}\n    → id: ${node.id}`;
  });

Based on learnings: "Tool nowledge_mem_connections must support graph exploration with edges: CRYSTALLIZED_FROM, EVOLVES (with sub-relations: supersedes/enriches/confirms/challenges), SOURCED_FROM, MENTIONS, showing strength % and memoryId."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/tools/connections.js` around lines 228 -
237, The MENTIONS branch omits node.id and weight; update the block handling
mentionConns (the grouped.get("MENTIONS") handling and the lines mapping that
uses getNodeTitle/node.getNodeType) to include the connected node's id and the
connection strength: for each ({ node, weight }) produce a line like "  - {name}
({type}) — id: {node.id}, strength: {Math.round(weight*100)}%" (or equivalent
percentage formatting) and push the section as before so MENTIONS matches the
other edge types by showing memoryId (node.id) and strength percent.

201-212: Two separate sections push the same "Knowledge evolution:" header — consider differentiating them.

If both graphExpand (lines 201–211) and graphEvolves (lines 282–303) return data, the output contains two adjacent "Knowledge evolution:" headings. Consider renaming one, e.g., "Knowledge evolution (neighbors):" vs "Knowledge evolution chain (full history):", or merging them.

Also applies to: 282-304

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/tools/connections.js` around lines 201 -
212, The two blocks that push the same "Knowledge evolution:" header (the
graphExpand branch that builds evolvesConns and the graphEvolves branch) should
produce distinct headings so they don't appear duplicated; update the header
string used when pushing into sections in the evolvesConns block (identify by
variables grouped, evolvesConns, getNodeTitle, EVOLVES_RELATION_LABELS,
sections) to something like "Knowledge evolution (neighbors):" and keep or set
the other block (graphEvolves) to "Knowledge evolution chain (full history):" or
merge logic into a single section if preferred so the UI shows a single, clearly
labeled evolution area.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js`:
- Around line 244-265: The handler buildAgentEndCaptureHandler currently ignores
the config parameter (named _cfg) and therefore does not enforce the required
opt-in guard; update the function to read the config object (e.g., rename _cfg
to cfg or destructure) and check that cfg.autoCapture === true before doing any
work (return early if not true), ensuring both the appendOrCreateThread
invocation and the triage/distill path only run when autoCapture is explicitly
enabled.
- Around line 267-285: buildConversationText currently returns unbounded text
(it uses fullContent) and you must truncate it before calling
client.triageConversation and client.distillThread to avoid huge LLM payloads;
update buildConversationText or add a truncation step right after const
conversationText = buildConversationText(result.normalized) to cap the total
length (e.g., 2,000–5,000 chars) or truncate individual fullContent fields
before assembling, then use the truncated conversationText for triage/distill
and for buildThreadTitle—refer to buildConversationText, conversationText,
client.triageConversation, and client.distillThread when applying the change.

---

Nitpick comments:
In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js`:
- Around line 154-217: The function appendOrCreateThread mixes implicit
undefined returns (bare return) and explicit null returns; change the early-exit
returns after the rawMessages check and after normalized.length === 0 to return
null so all failure/early-exit paths consistently return null (preserving
existing success returns like { threadId, normalized }); update any local
comments or JSDoc for appendOrCreateThread if present to reflect the unified
null sentinel.

In `@nowledge-mem-openclaw-plugin/src/tools/connections.js`:
- Around line 228-237: The MENTIONS branch omits node.id and weight; update the
block handling mentionConns (the grouped.get("MENTIONS") handling and the lines
mapping that uses getNodeTitle/node.getNodeType) to include the connected node's
id and the connection strength: for each ({ node, weight }) produce a line like
"  - {name} ({type}) — id: {node.id}, strength: {Math.round(weight*100)}%" (or
equivalent percentage formatting) and push the section as before so MENTIONS
matches the other edge types by showing memoryId (node.id) and strength percent.
- Around line 201-212: The two blocks that push the same "Knowledge evolution:"
header (the graphExpand branch that builds evolvesConns and the graphEvolves
branch) should produce distinct headings so they don't appear duplicated; update
the header string used when pushing into sections in the evolvesConns block
(identify by variables grouped, evolvesConns, getNodeTitle,
EVOLVES_RELATION_LABELS, sections) to something like "Knowledge evolution
(neighbors):" and keep or set the other block (graphEvolves) to "Knowledge
evolution chain (full history):" or merge logic into a single section if
preferred so the UI shows a single, clearly labeled evolution area.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83712f0 and f7de3fd.

📒 Files selected for processing (2)
  • nowledge-mem-openclaw-plugin/src/hooks/capture.js
  • nowledge-mem-openclaw-plugin/src/tools/connections.js

Comment thread nowledge-mem-openclaw-plugin/src/hooks/capture.js Outdated
Comment thread nowledge-mem-openclaw-plugin/src/hooks/capture.js
wey-gu and others added 2 commits February 23, 2026 19:45
1. capture.js: rename _cfg → cfg in buildAgentEndCaptureHandler, add
   defensive `if (!cfg.autoCapture) return` before triage/distill step.
   Registration in index.js already gates this, but the handler is now
   safe if called directly.
2. config.js: export API_DEFAULT_URL constant and isDefaultApiUrl()
   helper for local-vs-remote mode detection.
3. index.js: use isDefaultApiUrl() instead of hardcoded URL comparison.

Addresses codexstellaris review comments (round 2).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. client.js: patchWmSection() now uses exact heading match (===)
   instead of substring .includes(), preventing unintended matches
   (e.g. "## Note" matching "## Notebook").
2. config.js: unknown config keys now log a warning instead of
   throwing. OpenClaw may introduce new fields that we don't consume;
   crashing would break the plugin on platform updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
nowledge-mem-openclaw-plugin/src/hooks/capture.js (1)

224-228: buildConversationText still unbounded — previous review concern unresolved

buildConversationText maps over fullContent (untruncated raw text) with no ceiling. The only guard downstream is a 100-char floor at line 272. For sessions with large code blocks, the payload sent to both triageConversation and distillThread can still far exceed typical LLM context or HTTP payload limits.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js` around lines 224 - 228,
buildConversationText currently concatenates m.fullContent with no size cap,
letting very large messages (e.g., long code blocks) blow past LLM/HTTP limits;
change it to use a bounded/truncated variant of each message before joining
(e.g., prefer an existing m.truncatedContent or apply a truncate helper to
m.fullContent with a safe per-message max like 2k chars), so the outputs passed
to triageConversation and distillThread are size-limited; update
buildConversationText to reference the truncation (and ensure the helper
preserves useful context such as head/tail of code) rather than using
m.fullContent directly.
🧹 Nitpick comments (2)
nowledge-mem-openclaw-plugin/src/hooks/capture.js (1)

257-260: Optional: add a null guard on cfg for direct-call safety

buildAgentEndCaptureHandler is exported. If it is ever invoked directly (e.g. in tests) without a config object, cfg.autoCapture at line 260 throws a TypeError rather than cleanly skipping distillation.

🛡️ Proposed fix
 export function buildAgentEndCaptureHandler(client, cfg, logger) {
+	const _cfg = cfg ?? {};
 	return async (event, ctx) => {
 		if (!event?.success) return;

 		// 1. Always thread-append (idempotent, self-guards on empty messages).
 		const result = await appendOrCreateThread({
 			client,
 			logger,
 			event,
 			ctx,
 			reason: "agent_end",
 		});

 		// 2. Triage + distill
-		if (!cfg.autoCapture) return;
+		if (!_cfg.autoCapture) return;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js` around lines 257 - 260,
The handler checks cfg.autoCapture without guarding cfg, which can throw if
buildAgentEndCaptureHandler is called with no config; add a null/undefined guard
(e.g., if (!cfg || !cfg.autoCapture) return) at the start of the distillation
block so the function safely returns when cfg is falsy; update the check around
cfg.autoCapture in buildAgentEndCaptureHandler to handle a missing cfg object.
nowledge-mem-openclaw-plugin/src/client.js (1)

82-84: Hardcoded default URL repeated three times — extract a local constant

"http://127.0.0.1:14242" appears verbatim at lines 83, 172, and 186. config.js already exports API_DEFAULT_URL for this exact value, but client.js doesn't import it. At minimum, define a module-local constant to avoid the triple repetition.

♻️ Proposed fix
 import { spawnSync } from "node:child_process";

+const DEFAULT_API_URL = "http://127.0.0.1:14242";
+
 // ...

-		this._apiUrl =
-			(credentials.apiUrl || "").trim() || "http://127.0.0.1:14242";
+		this._apiUrl =
+			(credentials.apiUrl || "").trim() || DEFAULT_API_URL;

 // ...in _spawnEnv:
-		if (this._apiUrl !== "http://127.0.0.1:14242") {
+		if (this._apiUrl !== DEFAULT_API_URL) {

 // ...in _apiUrlArgs:
-		return this._apiUrl !== "http://127.0.0.1:14242"
+		return this._apiUrl !== DEFAULT_API_URL
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/client.js` around lines 82 - 84, The
hardcoded default URL "http://127.0.0.1:14242" is repeated in client.js (used to
set this._apiUrl) and should be centralized: import or reference the existing
API_DEFAULT_URL from config.js (or define a module-local constant named
API_DEFAULT_URL) and replace the literal occurrences where this._apiUrl is
assigned (currently using (credentials.apiUrl || "").trim() ||
"http://127.0.0.1:14242") and any other places that repeat the URL (lines around
172 and 186) to use the imported/constant value instead; ensure trimming and
fallback logic remains the same (e.g., (credentials.apiUrl || "").trim() ||
API_DEFAULT_URL) and update any related tests or usages if necessary.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nowledge-mem-openclaw-plugin/src/config.js`:
- Around line 23-30: The code currently logs unknown config keys (unknownKeys)
instead of rejecting them; change this to throw a descriptive Error when any
unknown keys are found during parsing of obj so the config parser remains strict
per guidelines (reference ALLOWED_KEYS and unknownKeys); ensure the thrown
message includes the list of unknown keys and context (e.g., which config
object) so typos like "autoRecal" surface immediately rather than being silently
ignored.

In `@nowledge-mem-openclaw-plugin/src/index.js`:
- Around line 49-51: Update the CLAUDE.md "Hook Surface" section to reflect the
modern hook name used in code: replace the legacy before_agent_start entry with
before_prompt_build (the same hook registered by api.on("before_prompt_build",
buildRecallHandler(client, cfg, logger)) when cfg.autoRecall is true); ensure
the documentation text and any example usage match the CHANGELOG.md and the
actual callsite (buildRecallHandler / before_prompt_build) so readers see the
current hook name and usage.

---

Duplicate comments:
In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js`:
- Around line 224-228: buildConversationText currently concatenates
m.fullContent with no size cap, letting very large messages (e.g., long code
blocks) blow past LLM/HTTP limits; change it to use a bounded/truncated variant
of each message before joining (e.g., prefer an existing m.truncatedContent or
apply a truncate helper to m.fullContent with a safe per-message max like 2k
chars), so the outputs passed to triageConversation and distillThread are
size-limited; update buildConversationText to reference the truncation (and
ensure the helper preserves useful context such as head/tail of code) rather
than using m.fullContent directly.

---

Nitpick comments:
In `@nowledge-mem-openclaw-plugin/src/client.js`:
- Around line 82-84: The hardcoded default URL "http://127.0.0.1:14242" is
repeated in client.js (used to set this._apiUrl) and should be centralized:
import or reference the existing API_DEFAULT_URL from config.js (or define a
module-local constant named API_DEFAULT_URL) and replace the literal occurrences
where this._apiUrl is assigned (currently using (credentials.apiUrl ||
"").trim() || "http://127.0.0.1:14242") and any other places that repeat the URL
(lines around 172 and 186) to use the imported/constant value instead; ensure
trimming and fallback logic remains the same (e.g., (credentials.apiUrl ||
"").trim() || API_DEFAULT_URL) and update any related tests or usages if
necessary.

In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js`:
- Around line 257-260: The handler checks cfg.autoCapture without guarding cfg,
which can throw if buildAgentEndCaptureHandler is called with no config; add a
null/undefined guard (e.g., if (!cfg || !cfg.autoCapture) return) at the start
of the distillation block so the function safely returns when cfg is falsy;
update the check around cfg.autoCapture in buildAgentEndCaptureHandler to handle
a missing cfg object.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7de3fd and e0483c9.

📒 Files selected for processing (4)
  • nowledge-mem-openclaw-plugin/src/client.js
  • nowledge-mem-openclaw-plugin/src/config.js
  • nowledge-mem-openclaw-plugin/src/hooks/capture.js
  • nowledge-mem-openclaw-plugin/src/index.js

Comment thread nowledge-mem-openclaw-plugin/src/config.js Outdated
Comment thread nowledge-mem-openclaw-plugin/src/index.js
1. config.js: Revert to strict unknown-key rejection (throw with
   descriptive error listing allowed keys) — matches CLAUDE.md
   Non-Goals contract.
2. CLAUDE.md: Fix hook name before_agent_start → before_prompt_build
   to match actual code in recall.js and index.js.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nowledge-mem-openclaw-plugin/CLAUDE.md (1)

72-80: ⚠️ Potential issue | 🟡 Minor

Update autoRecall default in the Config Keys table.

Line 76 lists the default as true, but src/config.js now defaults it to false. Please align the docs (and manifest defaults if applicable).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/CLAUDE.md` around lines 72 - 80, The docs
currently state the default for autoRecall as true but src/config.js now sets
autoRecall to false; update the Config Keys table in CLAUDE.md (the `autoRecall`
row) to show `false` as the default and, if there is a manifest or defaults
object elsewhere, make sure the manifest/defaults (the config export in
src/config.js and any package/manifest default block) consistently declare
autoRecall: false so the documentation and code match.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nowledge-mem-openclaw-plugin/CLAUDE.md`:
- Around line 33-34: Documentation and hook registration currently use the wrong
lifecycle hook: update the recall hook from before_prompt_build to
before_agent_start in both the code and docs. In index.js change the hook
registration that references before_prompt_build to before_agent_start (the
recall injection logic in recall.js stays the same but its registration point
must match), and update CLAUDE.md at the two mentioned locations (lines
referencing before_prompt_build around the recall description and the validator
scenarios) to say before_agent_start; also update any validator scenario names
or descriptions that assert before_prompt_build so tests/docs remain consistent.

---

Outside diff comments:
In `@nowledge-mem-openclaw-plugin/CLAUDE.md`:
- Around line 72-80: The docs currently state the default for autoRecall as true
but src/config.js now sets autoRecall to false; update the Config Keys table in
CLAUDE.md (the `autoRecall` row) to show `false` as the default and, if there is
a manifest or defaults object elsewhere, make sure the manifest/defaults (the
config export in src/config.js and any package/manifest default block)
consistently declare autoRecall: false so the documentation and code match.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0483c9 and c89d456.

📒 Files selected for processing (2)
  • nowledge-mem-openclaw-plugin/CLAUDE.md
  • nowledge-mem-openclaw-plugin/src/config.js

Comment thread nowledge-mem-openclaw-plugin/CLAUDE.md Outdated
wey-gu and others added 2 commits February 24, 2026 11:08
1. capture.js: Cap per-message content at 2000 chars and total
   conversation text at 30K chars before sending to triage/distill
   APIs — prevents unbounded payloads from long coding sessions.
2. index.js + recall.js: Change recall hook from before_prompt_build
   to before_agent_start (correct semantic phase for session-start
   context injection).
3. CLAUDE.md: Update hook surface docs to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All references to "before every turn" / "per turn" updated to
"at session start" across README, CLAUDE.md, and plugin manifest.
Also fix autoRecall default in CLAUDE.md config table (true → false).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nowledge-mem-openclaw-plugin/src/hooks/recall.js (1)

45-71: ⚠️ Potential issue | 🟡 Minor

Guard against nullish r.content before slicing.

Line 57 calls r.content.slice(0, 250) without a null check. If searchRich ever returns a result with a missing or undefined content field, this throws a TypeError. Compare with line 50 where r.title has a safe || "(untitled)" fallback.

🛡️ Proposed fix
-				const snippet = escapeForPrompt(r.content.slice(0, 250));
+				const snippet = escapeForPrompt((r.content || "").slice(0, 250));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/hooks/recall.js` around lines 45 - 71, The
results mapping in the recall logic calls r.content.slice(0, 250) without
guarding for null/undefined, which can throw; update the mapping in the
results.map callback (used while building lines for sections) to null-check
r.content before slicing and pass a safe string to escapeForPrompt (e.g., use an
empty string or "(no content)" fallback), so snippet =
escapeForPrompt((r.content || "").slice(0,250)) or equivalent; keep the other
fallbacks (r.title) and existing labels/score logic intact.
🧹 Nitpick comments (1)
nowledge-mem-openclaw-plugin/src/hooks/capture.js (1)

156-220: Inconsistent early-return types: undefined vs null.

Lines 158 and 165 return undefined implicitly (no messages / no valid normalized messages), while lines 200 and 218 return null explicitly on errors. The downstream guard at line 278 (!result) handles both correctly, so this isn't a bug. However, a consistent return convention (e.g., always null for "no result") would improve clarity for future callers.

♻️ Suggested: make early returns explicit
 async function appendOrCreateThread({ client, logger, event, ctx, reason }) {
 	const rawMessages = await resolveHookMessages(event);
-	if (!Array.isArray(rawMessages) || rawMessages.length === 0) return;
+	if (!Array.isArray(rawMessages) || rawMessages.length === 0) return null;
 
 	const threadId = buildStableThreadId(event, ctx);
 	const sessionKey = String(ctx?.sessionKey || ctx?.sessionId || "session");
 	const sessionId = String(ctx?.sessionId || "").trim();
 	const title = buildThreadTitle(ctx, reason);
 	const normalized = rawMessages.map(normalizeRoleMessage).filter(Boolean);
-	if (normalized.length === 0) return;
+	if (normalized.length === 0) return null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js` around lines 156 - 220,
The function appendOrCreateThread has inconsistent early-return types (implicit
undefined vs explicit null); change the early exits that currently do "return"
(after resolving rawMessages and after filtering normalized) to explicitly
"return null" so all non-success paths return null, keeping the existing null
returns in the catch blocks unchanged; update the two locations that check
rawMessages and normalized within appendOrCreateThread (the branches using
resolveHookMessages and normalized.map/filter) to return null.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@nowledge-mem-openclaw-plugin/src/hooks/recall.js`:
- Around line 45-71: The results mapping in the recall logic calls
r.content.slice(0, 250) without guarding for null/undefined, which can throw;
update the mapping in the results.map callback (used while building lines for
sections) to null-check r.content before slicing and pass a safe string to
escapeForPrompt (e.g., use an empty string or "(no content)" fallback), so
snippet = escapeForPrompt((r.content || "").slice(0,250)) or equivalent; keep
the other fallbacks (r.title) and existing labels/score logic intact.

---

Nitpick comments:
In `@nowledge-mem-openclaw-plugin/src/hooks/capture.js`:
- Around line 156-220: The function appendOrCreateThread has inconsistent
early-return types (implicit undefined vs explicit null); change the early exits
that currently do "return" (after resolving rawMessages and after filtering
normalized) to explicitly "return null" so all non-success paths return null,
keeping the existing null returns in the catch blocks unchanged; update the two
locations that check rawMessages and normalized within appendOrCreateThread (the
branches using resolveHookMessages and normalized.map/filter) to return null.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c89d456 and 9f77fe9.

📒 Files selected for processing (3)
  • nowledge-mem-openclaw-plugin/src/hooks/capture.js
  • nowledge-mem-openclaw-plugin/src/hooks/recall.js
  • nowledge-mem-openclaw-plugin/src/index.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • nowledge-mem-openclaw-plugin/src/index.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant