Conversation
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>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe 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
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
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
contextunit type is missing from the top-level tool description.
VALID_UNIT_TYPEScontains 8 entries includingcontext, and theunit_typeproperty's own description (Lines 51–53) lists it correctly. But the top-leveldescriptionstring on Lines 32–34 enumerates only 7 types and omitscontext. An LLM reading only the tool-level description may never consider emittingcontextasunit_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
EVOLVESandSOURCED_FROMneighbor sections omit strength %.
CRYSTALLIZED_FROMrenders[${(weight * 100).toFixed(0)}%]from theweightfield, butEVOLVES(lines 203–210) andSOURCED_FROM(lines 217–225) both destructure theweightfrom 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_connectionsmust 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
MENTIONSconnections are missing bothmemoryIdand 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 theMENTIONSblock 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_connectionsmust 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 theautoRecalldependency in the UI hint formaxRecallResults.The
configSchemadescription (line 46) correctly notes "only used when autoRecall is enabled", but theuiHints.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 fromappendOrCreateThread.Early-exit paths (no messages / empty normalized list) return
undefinedwhile error paths returnnull. 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:titlefield indistillThreadis not coerced toString.All other fields in the same payload (
thread_id,thread_content) useString(x || "").thread_title: title || ""skips theString()coercion. Whiletitleis typed asstring?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
⛔ Files ignored due to path filters (1)
nowledge-mem-openclaw-plugin/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (14)
nowledge-mem-openclaw-plugin/.gitignorenowledge-mem-openclaw-plugin/CHANGELOG.mdnowledge-mem-openclaw-plugin/README.mdnowledge-mem-openclaw-plugin/openclaw.plugin.jsonnowledge-mem-openclaw-plugin/package.jsonnowledge-mem-openclaw-plugin/src/client.jsnowledge-mem-openclaw-plugin/src/config.jsnowledge-mem-openclaw-plugin/src/hooks/capture.jsnowledge-mem-openclaw-plugin/src/hooks/recall.jsnowledge-mem-openclaw-plugin/src/index.jsnowledge-mem-openclaw-plugin/src/tools/connections.jsnowledge-mem-openclaw-plugin/src/tools/context.jsnowledge-mem-openclaw-plugin/src/tools/memory-search.jsnowledge-mem-openclaw-plugin/src/tools/save.js
…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>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
nowledge-mem-openclaw-plugin/src/hooks/capture.js (1)
154-217: Inconsistent implicit vs. explicitnullreturns in early-exit pathsLines 156 and 163 return
undefined(barereturn;), while the error paths at lines 198 and 216 explicitly returnnull. 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:MENTIONSconnections omit bothnode.idand 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
weightandnode.idare 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_connectionsmust 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) andgraphEvolves(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
📒 Files selected for processing (2)
nowledge-mem-openclaw-plugin/src/hooks/capture.jsnowledge-mem-openclaw-plugin/src/tools/connections.js
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>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
nowledge-mem-openclaw-plugin/src/hooks/capture.js (1)
224-228:buildConversationTextstill unbounded — previous review concern unresolved
buildConversationTextmaps overfullContent(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 bothtriageConversationanddistillThreadcan 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 oncfgfor direct-call safety
buildAgentEndCaptureHandleris exported. If it is ever invoked directly (e.g. in tests) without a config object,cfg.autoCaptureat line 260 throws aTypeErrorrather 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.jsalready exportsAPI_DEFAULT_URLfor this exact value, butclient.jsdoesn'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
📒 Files selected for processing (4)
nowledge-mem-openclaw-plugin/src/client.jsnowledge-mem-openclaw-plugin/src/config.jsnowledge-mem-openclaw-plugin/src/hooks/capture.jsnowledge-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>
There was a problem hiding this comment.
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 | 🟡 MinorUpdate
autoRecalldefault in the Config Keys table.Line 76 lists the default as
true, butsrc/config.jsnow defaults it tofalse. 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.
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>
There was a problem hiding this comment.
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 | 🟡 MinorGuard against nullish
r.contentbefore slicing.Line 57 calls
r.content.slice(0, 250)without a null check. IfsearchRichever returns a result with a missing orundefinedcontentfield, this throws aTypeError. Compare with line 50 wherer.titlehas 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:undefinedvsnull.Lines 158 and 165 return
undefinedimplicitly (no messages / no valid normalized messages), while lines 200 and 218 returnnullexplicitly 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., alwaysnullfor "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
📒 Files selected for processing (3)
nowledge-mem-openclaw-plugin/src/hooks/capture.jsnowledge-mem-openclaw-plugin/src/hooks/recall.jsnowledge-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
Summary by CodeRabbit
Release Notes v0.3.0
New Features
Configuration Changes
Documentation