neuralmemory 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,7 +54,7 @@ Add to `~/.openclaw/openclaw.json`:
54
54
 
55
55
  **v1.7.0+**: The plugin dynamically fetches **all tools** from the MCP server at startup. Whatever version of `neural-memory` you have installed, the plugin automatically exposes every tool it provides — no plugin update needed when new tools are added.
56
56
 
57
- With `neural-memory>=4.6.0`, this includes **45 tools**:
57
+ With `neural-memory>=4.6.0`, this includes **50 tools**:
58
58
 
59
59
  | Category | Tools |
60
60
  |----------|-------|
package/dist/index.d.ts CHANGED
@@ -28,14 +28,28 @@
28
28
  * Registers:
29
29
  * N tools — dynamically from MCP server (fallback: 5 core + 2 compat)
30
30
  * 1 service — MCP process lifecycle (start/stop)
31
- * 2 hooks — before_agent_start (auto-context), agent_end (auto-capture)
31
+ * 6 hooks — before_agent_start (auto-context), agent_end (auto-capture),
32
+ * session:compact:before (flush), command:new/reset (flush),
33
+ * gateway:startup (consolidation)
32
34
  */
33
35
  import type { OpenClawPluginDefinition } from "./types.js";
36
+ /**
37
+ * Strip metadata preamble from raw prompts before recall.
38
+ *
39
+ * OpenClaw + Telegram injects JSON metadata, NeuralMemory context blocks,
40
+ * env vars, and system boilerplate into ev.prompt. Passing these raw to
41
+ * nmem_recall creates junk neurons like "[concept] json message id".
42
+ *
43
+ * Stripping order matters — later passes clean up residue from earlier ones.
44
+ */
45
+ export declare function stripPromptMetadata(raw: string): string;
34
46
  type PluginConfig = {
35
47
  pythonPath: string;
36
48
  brain: string;
37
49
  autoContext: boolean;
38
50
  autoCapture: boolean;
51
+ autoFlush: boolean;
52
+ autoConsolidate: boolean;
39
53
  contextDepth: number;
40
54
  maxContextTokens: number;
41
55
  timeout: number;
package/dist/index.js CHANGED
@@ -28,10 +28,46 @@
28
28
  * Registers:
29
29
  * N tools — dynamically from MCP server (fallback: 5 core + 2 compat)
30
30
  * 1 service — MCP process lifecycle (start/stop)
31
- * 2 hooks — before_agent_start (auto-context), agent_end (auto-capture)
31
+ * 6 hooks — before_agent_start (auto-context), agent_end (auto-capture),
32
+ * session:compact:before (flush), command:new/reset (flush),
33
+ * gateway:startup (consolidation)
32
34
  */
33
35
  import { NeuralMemoryMcpClient } from "./mcp-client.js";
34
36
  import { createToolsFromMcp, createFallbackTools, createCompatibilityTools } from "./tools.js";
37
+ // ── Prompt metadata stripping ─────────────────────────────
38
+ /**
39
+ * Strip metadata preamble from raw prompts before recall.
40
+ *
41
+ * OpenClaw + Telegram injects JSON metadata, NeuralMemory context blocks,
42
+ * env vars, and system boilerplate into ev.prompt. Passing these raw to
43
+ * nmem_recall creates junk neurons like "[concept] json message id".
44
+ *
45
+ * Stripping order matters — later passes clean up residue from earlier ones.
46
+ */
47
+ export function stripPromptMetadata(raw) {
48
+ let cleaned = raw;
49
+ // 1. Remove JSON blocks (Telegram metadata, conversation info)
50
+ cleaned = cleaned.replace(/^\{[\s\S]*?"(?:conversation|message_id|sender_id|sender|chat_id|update_id)"[\s\S]*?\}$/gm, "");
51
+ // 2. Remove NeuralMemory context sections (## Relevant Memories, etc.)
52
+ // The |$ ensures sections at end-of-string are also stripped.
53
+ cleaned = cleaned.replace(/^#{1,3}\s*(?:Relevant Memories|Related Information|Relevant Context|Neural Memory)[\s\S]*?(?=\n#{1,3}\s|\n\n(?![-•*\s])|$)/gim, "");
54
+ // 3. Remove neuron-type bullet lines injected by NM context
55
+ cleaned = cleaned.replace(/^-\s*\[(?:concept|entity|decision|error|preference|insight|memory|fact|workflow|instruction|pattern)\].*$/gim, "");
56
+ // 4. Remove [NeuralMemory — ...] wrapper lines
57
+ cleaned = cleaned.replace(/^\[NeuralMemory\s*[—–-].*\]$/gm, "");
58
+ // 5. Remove metadata labels (untrusted metadata lines)
59
+ cleaned = cleaned.replace(/^(?:Conversation info|Sender|Context|System)\s*\(.*?\)\s*:?\s*$/gim, "");
60
+ // 6. Remove env/export lines
61
+ cleaned = cleaned.replace(/^export\s+\w+=.*$/gm, "");
62
+ // 7. Collapse whitespace runs
63
+ cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
64
+ // Fallback: if everything was stripped, use last non-empty line of raw
65
+ if (!cleaned) {
66
+ const lines = raw.split("\n").filter((l) => l.trim());
67
+ cleaned = lines[lines.length - 1]?.trim() ?? raw.trim();
68
+ }
69
+ return cleaned;
70
+ }
35
71
  // ── System prompt for tool awareness ──────────────────────
36
72
  /**
37
73
  * Build a system prompt listing all registered tool names.
@@ -41,21 +77,43 @@ function buildToolInstructions(tools) {
41
77
  const toolList = tools
42
78
  .map((t) => `- ${t.name}: ${t.description.slice(0, 100)}`)
43
79
  .join("\n");
44
- return `You have NeuralMemory tools for persistent memory across sessions. Call these as TOOL CALLS (not CLI commands):
45
-
46
- ${toolList}
47
-
48
- NeuralMemory (nmem_*) is your primary memory system. memory_search and memory_get are legacy aliases that redirect to nmem_recall — prefer nmem_* tools directly for full functionality.
49
-
50
- These are tool calls, NOT shell commands. Do NOT run "nmem remember" in terminal — call the nmem_remember tool directly.
51
-
52
- PROACTIVE MEMORY: Use nmem_remember after decisions, errors, and insights. Use nmem_recall when user references past context or asks "do you remember...". Use nmem_remember_batch to store multiple memories at once.`;
80
+ return `Neural Memory gives you persistent memory across sessions. Use it proactively each session starts fresh, so without explicit saves ALL discoveries are lost.
81
+
82
+ These are TOOL CALLS, not CLI commands. Do NOT run "nmem remember" in terminal.
83
+
84
+ ## Available Tools
85
+ ${toolList}
86
+
87
+ nmem_* is your primary memory system. memory_search/memory_get are legacy aliases for nmem_recall.
88
+
89
+ ## WHEN TO RECALL
90
+ - New session starts → nmem_recall("current project context")
91
+ - User references past event → nmem_recall("<that topic>")
92
+ - Prefix queries with project name for precision
93
+
94
+ ## WHEN TO SAVE
95
+ After each task: did you make a decision (type="decision", priority=7), fix a bug (type="error", priority=7), learn a preference (type="preference", priority=8), or discover an insight (type="insight", priority=6)?
96
+
97
+ Save with: nmem_remember(content="Chose X over Y because Z", type="decision", priority=7, tags=["project", "topic"])
98
+
99
+ ## CONTENT QUALITY
100
+ - Max 1-3 sentences. Use causal language: "Chose X because Y", "Root cause was X, fixed by Y".
101
+ - Always include project name + topic in tags (lowercase).
102
+ - For temporary scratch notes: nmem_remember(content="...", ephemeral=true) — auto-expires, never synced.
103
+
104
+ ## SESSION END
105
+ nmem_auto(action="process", text="<brief session summary>")
106
+
107
+ ## COMPACT MODE
108
+ All tools support compact=true (saves 60-80% tokens) and token_budget=N.`;
53
109
  }
54
110
  const DEFAULT_CONFIG = {
55
111
  pythonPath: "python",
56
112
  brain: "default",
57
113
  autoContext: true,
58
114
  autoCapture: true,
115
+ autoFlush: true,
116
+ autoConsolidate: true,
59
117
  contextDepth: 1,
60
118
  maxContextTokens: 500,
61
119
  timeout: 30_000,
@@ -78,6 +136,12 @@ export function resolveConfig(raw) {
78
136
  autoCapture: typeof merged.autoCapture === "boolean"
79
137
  ? merged.autoCapture
80
138
  : DEFAULT_CONFIG.autoCapture,
139
+ autoFlush: typeof merged.autoFlush === "boolean"
140
+ ? merged.autoFlush
141
+ : DEFAULT_CONFIG.autoFlush,
142
+ autoConsolidate: typeof merged.autoConsolidate === "boolean"
143
+ ? merged.autoConsolidate
144
+ : DEFAULT_CONFIG.autoConsolidate,
81
145
  contextDepth: typeof merged.contextDepth === "number" &&
82
146
  Number.isInteger(merged.contextDepth) &&
83
147
  merged.contextDepth >= 0 &&
@@ -112,7 +176,7 @@ function getOrCreateMcpClient(cfg, logger) {
112
176
  const key = `${cfg.pythonPath}::${cfg.brain}`;
113
177
  const existing = mcpClients.get(key);
114
178
  if (existing) {
115
- logger.info(`Reusing existing MCP client for brain "${cfg.brain}"`);
179
+ logger.debug?.(`Reusing existing MCP client for brain "${cfg.brain}"`);
116
180
  return existing;
117
181
  }
118
182
  const mcp = new NeuralMemoryMcpClient({
@@ -130,7 +194,7 @@ const plugin = {
130
194
  id: "neuralmemory",
131
195
  name: "NeuralMemory",
132
196
  description: "Brain-inspired persistent memory for AI agents — neurons, synapses, and fibers",
133
- version: "1.14.0",
197
+ version: "1.15.0",
134
198
  kind: "memory",
135
199
  register(api) {
136
200
  const cfg = resolveConfig(api.pluginConfig);
@@ -186,8 +250,9 @@ const plugin = {
186
250
  if (cfg.autoContext && mcp.connected) {
187
251
  const ev = event;
188
252
  try {
253
+ const query = stripPromptMetadata(ev.prompt);
189
254
  const raw = await mcp.callTool("nmem_recall", {
190
- query: ev.prompt,
255
+ query,
191
256
  depth: cfg.contextDepth,
192
257
  max_tokens: cfg.maxContextTokens,
193
258
  });
@@ -232,9 +297,78 @@ const plugin = {
232
297
  }
233
298
  }, { priority: 90 });
234
299
  }
300
+ // ── Hook: flush memories before context compaction ──
301
+ if (cfg.autoFlush) {
302
+ api.on("session:compact:before", async (_event, _ctx) => {
303
+ if (!mcp.connected)
304
+ return;
305
+ try {
306
+ await mcp.callTool("nmem_auto", {
307
+ action: "process",
308
+ text: "[pre-compact emergency flush]",
309
+ });
310
+ api.logger.info("Pre-compact flush completed");
311
+ }
312
+ catch (err) {
313
+ api.logger.warn(`Pre-compact flush failed: ${err.message}`);
314
+ }
315
+ }, { priority: 5 });
316
+ // Flush on session boundary (new/reset commands)
317
+ api.on("command:new", async (_event, _ctx) => {
318
+ if (!mcp.connected)
319
+ return;
320
+ try {
321
+ await mcp.callTool("nmem_auto", {
322
+ action: "process",
323
+ text: "[session boundary — command:new]",
324
+ });
325
+ }
326
+ catch (err) {
327
+ api.logger.warn(`Session boundary flush failed: ${err.message}`);
328
+ }
329
+ }, { priority: 10 });
330
+ api.on("command:reset", async (_event, _ctx) => {
331
+ if (!mcp.connected)
332
+ return;
333
+ try {
334
+ await mcp.callTool("nmem_auto", {
335
+ action: "process",
336
+ text: "[session boundary — command:reset]",
337
+ });
338
+ }
339
+ catch (err) {
340
+ api.logger.warn(`Session reset flush failed: ${err.message}`);
341
+ }
342
+ }, { priority: 10 });
343
+ }
344
+ // ── Hook: consolidation on gateway startup ───────────
345
+ if (cfg.autoConsolidate) {
346
+ api.on("gateway:startup", async (_event, _ctx) => {
347
+ if (!mcp.connected) {
348
+ try {
349
+ await mcp.ensureConnected();
350
+ }
351
+ catch (err) {
352
+ api.logger.warn(`MCP connect on startup failed: ${err.message}`);
353
+ return;
354
+ }
355
+ }
356
+ try {
357
+ await mcp.callTool("nmem_consolidate", {
358
+ strategy: "enrich",
359
+ compact: true,
360
+ });
361
+ api.logger.info("Startup consolidation completed");
362
+ }
363
+ catch (err) {
364
+ api.logger.warn(`Startup consolidation failed: ${err.message}`);
365
+ }
366
+ }, { priority: 50 });
367
+ }
235
368
  // ── Done ────────────────────────────────────────────
236
369
  api.logger.info(`NeuralMemory registered (brain: ${cfg.brain}, ` +
237
- `autoContext: ${cfg.autoContext}, autoCapture: ${cfg.autoCapture}) ` +
370
+ `autoContext: ${cfg.autoContext}, autoCapture: ${cfg.autoCapture}, ` +
371
+ `autoFlush: ${cfg.autoFlush}, autoConsolidate: ${cfg.autoConsolidate}) — ` +
238
372
  `tools will be loaded dynamically from MCP on service start`);
239
373
  },
240
374
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -41,6 +41,19 @@ export type AgentContext = {
41
41
  sessionKey?: string;
42
42
  workspaceDir?: string;
43
43
  };
44
+ export type SessionCompactEvent = {
45
+ reason?: string;
46
+ messagesBeforeCompact?: number;
47
+ };
48
+ export type CommandEvent = {
49
+ command: string;
50
+ sessionKey?: string;
51
+ workspaceDir?: string;
52
+ };
53
+ export type GatewayStartupEvent = {
54
+ version?: string;
55
+ workspaceDir?: string;
56
+ };
44
57
  export type OpenClawPluginApi = {
45
58
  id: string;
46
59
  name: string;
@@ -3,7 +3,7 @@
3
3
  "kind": "memory",
4
4
  "name": "NeuralMemory",
5
5
  "description": "Brain-inspired persistent memory for AI agents — neurons, synapses, and fibers. REQUIRED: set plugins.slots.memory = \"neuralmemory\" in openclaw.json to disable the default memory-core plugin and activate NeuralMemory as the exclusive memory provider.",
6
- "version": "1.14.0",
6
+ "version": "1.15.0",
7
7
  "configSchema": {
8
8
  "jsonSchema": {
9
9
  "type": "object",
@@ -30,6 +30,16 @@
30
30
  "description": "Auto-extract and store memories after each agent run",
31
31
  "default": true
32
32
  },
33
+ "autoFlush": {
34
+ "type": "boolean",
35
+ "description": "Flush memories before context compaction and on session boundaries (new/reset)",
36
+ "default": true
37
+ },
38
+ "autoConsolidate": {
39
+ "type": "boolean",
40
+ "description": "Run consolidation on gateway startup to enrich and merge memories",
41
+ "default": true
42
+ },
33
43
  "contextDepth": {
34
44
  "type": "integer",
35
45
  "minimum": 0,
@@ -76,6 +86,14 @@
76
86
  "label": "Auto-capture Memories",
77
87
  "help": "Automatically extract facts, decisions, and insights after each agent run"
78
88
  },
89
+ "autoFlush": {
90
+ "label": "Auto-flush on Compact/Reset",
91
+ "help": "Flush unsaved memories before context compaction and when starting a new session"
92
+ },
93
+ "autoConsolidate": {
94
+ "label": "Auto-consolidate on Startup",
95
+ "help": "Run memory consolidation (enrich strategy) when the OpenClaw gateway starts"
96
+ },
79
97
  "contextDepth": {
80
98
  "label": "Context Depth",
81
99
  "help": "How deep to search: 0=instant lookup, 1=contextual, 2=habit patterns, 3=full graph traversal"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuralmemory",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "NeuralMemory plugin for OpenClaw — brain-inspired persistent memory for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -1,342 +1,534 @@
1
- /**
2
- * NeuralMemory — OpenClaw Memory Plugin
3
- *
4
- * Brain-inspired persistent memory for AI agents.
5
- * Occupies the exclusive "memory" plugin slot.
6
- *
7
- * Architecture:
8
- * OpenClaw ←→ Plugin (TypeScript) ←→ MCP stdio ←→ NeuralMemory (Python)
9
- *
10
- * v1.7.0: Dynamic tool proxy — fetches all tools from MCP `tools/list`
11
- * instead of hardcoding 6 tools. Automatically exposes every tool the
12
- * MCP server provides (39+ tools in NM v2.28.0).
13
- *
14
- * v1.8.0: Compatible with NM v2.29.0 — RRF score fusion, graph-based
15
- * query expansion, and Personalized PageRank activation.
16
- *
17
- * v1.8.1: Fix async register() — OpenClaw requires synchronous registration.
18
- * Fallback tools registered sync; MCP connection deferred to service.start().
19
- *
20
- * v1.9.0: Backward-compat shim tools (memory_search, memory_get) to prevent
21
- * "allowList contains unknown entries" warnings when NM replaces memory-core.
22
- *
23
- * v1.10.0: Singleton MCP client — multiple workspaces (multi-agent) share
24
- * the same connected client instance, keyed by (pythonPath, brain). Fixes
25
- * "NeuralMemory service not running" when OpenClaw registers the plugin
26
- * for a second workspace after gateway startup.
27
- *
28
- * Registers:
29
- * N tools — dynamically from MCP server (fallback: 5 core + 2 compat)
30
- * 1 service — MCP process lifecycle (start/stop)
31
- * 2 hooks — before_agent_start (auto-context), agent_end (auto-capture)
32
- */
33
-
34
- import type {
35
- OpenClawPluginDefinition,
36
- OpenClawPluginApi,
37
- BeforeAgentStartEvent,
38
- BeforeAgentStartResult,
39
- AgentEndEvent,
40
- } from "./types.js";
41
- import { NeuralMemoryMcpClient } from "./mcp-client.js";
42
- import type { PluginLogger } from "./types.js";
43
- import { createToolsFromMcp, createFallbackTools, createCompatibilityTools } from "./tools.js";
44
- import type { ToolDefinition } from "./tools.js";
45
-
46
- // ── System prompt for tool awareness ──────────────────────
47
-
48
- /**
49
- * Build a system prompt listing all registered tool names.
50
- * This makes the agent aware of which nmem_* tools are available.
51
- */
52
- function buildToolInstructions(tools: ToolDefinition[]): string {
53
- const toolList = tools
54
- .map((t) => `- ${t.name}: ${t.description.slice(0, 100)}`)
55
- .join("\n");
56
-
57
- return `You have NeuralMemory tools for persistent memory across sessions. Call these as TOOL CALLS (not CLI commands):
58
-
59
- ${toolList}
60
-
61
- NeuralMemory (nmem_*) is your primary memory system. memory_search and memory_get are legacy aliases that redirect to nmem_recall — prefer nmem_* tools directly for full functionality.
62
-
63
- These are tool calls, NOT shell commands. Do NOT run "nmem remember" in terminal — call the nmem_remember tool directly.
64
-
65
- PROACTIVE MEMORY: Use nmem_remember after decisions, errors, and insights. Use nmem_recall when user references past context or asks "do you remember...". Use nmem_remember_batch to store multiple memories at once.`;
66
- }
67
-
68
- // ── Config ─────────────────────────────────────────────────
69
-
70
- type PluginConfig = {
71
- pythonPath: string;
72
- brain: string;
73
- autoContext: boolean;
74
- autoCapture: boolean;
75
- contextDepth: number;
76
- maxContextTokens: number;
77
- timeout: number;
78
- initTimeout: number;
79
- };
80
-
81
- const DEFAULT_CONFIG: Readonly<PluginConfig> = {
82
- pythonPath: "python",
83
- brain: "default",
84
- autoContext: true,
85
- autoCapture: true,
86
- contextDepth: 1,
87
- maxContextTokens: 500,
88
- timeout: 30_000,
89
- initTimeout: 90_000,
90
- };
91
-
92
- export const BRAIN_NAME_RE = /^[a-zA-Z0-9_\-.]{1,64}$/;
93
- export const MAX_AUTO_CAPTURE_CHARS = 50_000;
94
-
95
- export function resolveConfig(raw?: Record<string, unknown>): PluginConfig {
96
- const merged = { ...DEFAULT_CONFIG, ...(raw ?? {}) };
97
-
98
- return {
99
- pythonPath:
100
- typeof merged.pythonPath === "string" && merged.pythonPath.length > 0
101
- ? merged.pythonPath
102
- : DEFAULT_CONFIG.pythonPath,
103
- brain:
104
- typeof merged.brain === "string" && BRAIN_NAME_RE.test(merged.brain)
105
- ? merged.brain
106
- : DEFAULT_CONFIG.brain,
107
- autoContext:
108
- typeof merged.autoContext === "boolean"
109
- ? merged.autoContext
110
- : DEFAULT_CONFIG.autoContext,
111
- autoCapture:
112
- typeof merged.autoCapture === "boolean"
113
- ? merged.autoCapture
114
- : DEFAULT_CONFIG.autoCapture,
115
- contextDepth:
116
- typeof merged.contextDepth === "number" &&
117
- Number.isInteger(merged.contextDepth) &&
118
- merged.contextDepth >= 0 &&
119
- merged.contextDepth <= 3
120
- ? merged.contextDepth
121
- : DEFAULT_CONFIG.contextDepth,
122
- maxContextTokens:
123
- typeof merged.maxContextTokens === "number" &&
124
- Number.isInteger(merged.maxContextTokens) &&
125
- merged.maxContextTokens >= 100 &&
126
- merged.maxContextTokens <= 10_000
127
- ? merged.maxContextTokens
128
- : DEFAULT_CONFIG.maxContextTokens,
129
- timeout:
130
- typeof merged.timeout === "number" &&
131
- Number.isFinite(merged.timeout) &&
132
- merged.timeout >= 5_000 &&
133
- merged.timeout <= 120_000
134
- ? merged.timeout
135
- : DEFAULT_CONFIG.timeout,
136
- initTimeout:
137
- typeof merged.initTimeout === "number" &&
138
- Number.isFinite(merged.initTimeout) &&
139
- merged.initTimeout >= 10_000 &&
140
- merged.initTimeout <= 300_000
141
- ? merged.initTimeout
142
- : DEFAULT_CONFIG.initTimeout,
143
- };
144
- }
145
-
146
- // ── Singleton MCP client pool ────────────────────────────────
147
- // Multiple workspaces may call register() independently, but all
148
- // should share the same MCP process per (pythonPath, brain) combo.
149
-
150
- const mcpClients = new Map<string, NeuralMemoryMcpClient>();
151
-
152
- function getOrCreateMcpClient(
153
- cfg: PluginConfig,
154
- logger: PluginLogger,
155
- ): NeuralMemoryMcpClient {
156
- const key = `${cfg.pythonPath}::${cfg.brain}`;
157
-
158
- const existing = mcpClients.get(key);
159
- if (existing) {
160
- logger.info(`Reusing existing MCP client for brain "${cfg.brain}"`);
161
- return existing;
162
- }
163
-
164
- const mcp = new NeuralMemoryMcpClient({
165
- pythonPath: cfg.pythonPath,
166
- brain: cfg.brain,
167
- logger,
168
- timeout: cfg.timeout,
169
- initTimeout: cfg.initTimeout,
170
- });
171
-
172
- mcpClients.set(key, mcp);
173
- return mcp;
174
- }
175
-
176
- // ── Plugin definition ──────────────────────────────────────
177
-
178
- const plugin: OpenClawPluginDefinition = {
179
- id: "neuralmemory",
180
- name: "NeuralMemory",
181
- description:
182
- "Brain-inspired persistent memory for AI agents neurons, synapses, and fibers",
183
- version: "1.14.0",
184
- kind: "memory",
185
-
186
- register(api: OpenClawPluginApi): void {
187
- const cfg = resolveConfig(api.pluginConfig);
188
-
189
- const mcp = getOrCreateMcpClient(cfg, api.logger);
190
-
191
- // ── Register fallback tools synchronously ────────────
192
- // OpenClaw requires register() to be synchronous.
193
- // Register stable fallback tools immediately; MCP connection
194
- // and dynamic tool discovery happen in service.start().
195
- // Fallback tools auto-reconnect MCP on first call.
196
-
197
- const registeredTools = createFallbackTools(mcp);
198
- const compatTools = createCompatibilityTools(mcp);
199
-
200
- for (const t of [...registeredTools, ...compatTools]) {
201
- api.registerTool(t, { name: t.name });
202
- }
203
-
204
- api.logger.info(
205
- `Registered ${registeredTools.length} NeuralMemory tools + ${compatTools.length} compat shims (sync)`,
206
- );
207
-
208
- // ── Service: MCP process lifecycle ───────────────────
209
-
210
- api.registerService({
211
- id: "neuralmemory-mcp",
212
-
213
- async start(): Promise<void> {
214
- if (!mcp.connected) {
215
- try {
216
- await mcp.connect();
217
- api.logger.info("NeuralMemory MCP connected in service.start()");
218
-
219
- // Log discovered tools for diagnostics (cannot re-register
220
- // after register() — OpenClaw freezes the tool list).
221
- try {
222
- const dynamicTools = await createToolsFromMcp(mcp);
223
- api.logger.info(
224
- `NeuralMemory MCP discovered ${dynamicTools.length} tools`,
225
- );
226
- } catch (err) {
227
- api.logger.warn(
228
- `Tool discovery failed: ${(err as Error).message}`,
229
- );
230
- }
231
- } catch (err) {
232
- api.logger.error(
233
- `Failed to start NeuralMemory MCP: ${(err as Error).message}`,
234
- );
235
- throw err;
236
- }
237
- }
238
- },
239
-
240
- async stop(): Promise<void> {
241
- // Remove from singleton pool so next register() creates fresh client
242
- const key = `${cfg.pythonPath}::${cfg.brain}`;
243
- mcpClients.delete(key);
244
- await mcp.close();
245
- api.logger.info("NeuralMemory MCP service stopped");
246
- },
247
- });
248
-
249
- // ── Hook: tool awareness + auto-context before agent start ───
250
-
251
- api.on(
252
- "before_agent_start",
253
- async (
254
- event: unknown,
255
- _ctx: unknown,
256
- ): Promise<BeforeAgentStartResult | void> => {
257
- const result: BeforeAgentStartResult = {
258
- systemPrompt: buildToolInstructions(registeredTools),
259
- };
260
-
261
- if (cfg.autoContext && mcp.connected) {
262
- const ev = event as BeforeAgentStartEvent;
263
-
264
- try {
265
- const raw = await mcp.callTool("nmem_recall", {
266
- query: ev.prompt,
267
- depth: cfg.contextDepth,
268
- max_tokens: cfg.maxContextTokens,
269
- });
270
-
271
- const data = JSON.parse(raw) as {
272
- answer?: string;
273
- confidence?: number;
274
- };
275
-
276
- if (data.answer && (data.confidence ?? 0) > 0.1) {
277
- result.prependContext = `[NeuralMemory — relevant context]\n${data.answer}`;
278
- }
279
- } catch (err) {
280
- api.logger.warn(
281
- `Auto-context failed: ${(err as Error).message}`,
282
- );
283
- }
284
- }
285
-
286
- return result;
287
- },
288
- { priority: 10 },
289
- );
290
-
291
- // ── Hook: auto-capture after agent completes ────────
292
-
293
- if (cfg.autoCapture) {
294
- api.on(
295
- "agent_end",
296
- async (event: unknown, _ctx: unknown): Promise<void> => {
297
- if (!mcp.connected) return;
298
-
299
- const ev = event as AgentEndEvent;
300
- if (!ev.success) return;
301
-
302
- try {
303
- const messages = ev.messages?.slice(-5) ?? [];
304
- const text = messages
305
- .filter(
306
- (m: unknown): m is { role: string; content: string } =>
307
- typeof m === "object" &&
308
- m !== null &&
309
- (m as { role?: string }).role === "assistant" &&
310
- typeof (m as { content?: unknown }).content === "string",
311
- )
312
- .map((m) => m.content)
313
- .join("\n")
314
- .slice(0, MAX_AUTO_CAPTURE_CHARS);
315
-
316
- if (text.length > 50) {
317
- await mcp.callTool("nmem_auto", {
318
- action: "process",
319
- text,
320
- });
321
- }
322
- } catch (err) {
323
- api.logger.warn(
324
- `Auto-capture failed: ${(err as Error).message}`,
325
- );
326
- }
327
- },
328
- { priority: 90 },
329
- );
330
- }
331
-
332
- // ── Done ────────────────────────────────────────────
333
-
334
- api.logger.info(
335
- `NeuralMemory registered (brain: ${cfg.brain}, ` +
336
- `autoContext: ${cfg.autoContext}, autoCapture: ${cfg.autoCapture}) — ` +
337
- `tools will be loaded dynamically from MCP on service start`,
338
- );
339
- },
340
- };
341
-
342
- export default plugin;
1
+ /**
2
+ * NeuralMemory — OpenClaw Memory Plugin
3
+ *
4
+ * Brain-inspired persistent memory for AI agents.
5
+ * Occupies the exclusive "memory" plugin slot.
6
+ *
7
+ * Architecture:
8
+ * OpenClaw ←→ Plugin (TypeScript) ←→ MCP stdio ←→ NeuralMemory (Python)
9
+ *
10
+ * v1.7.0: Dynamic tool proxy — fetches all tools from MCP `tools/list`
11
+ * instead of hardcoding 6 tools. Automatically exposes every tool the
12
+ * MCP server provides (39+ tools in NM v2.28.0).
13
+ *
14
+ * v1.8.0: Compatible with NM v2.29.0 — RRF score fusion, graph-based
15
+ * query expansion, and Personalized PageRank activation.
16
+ *
17
+ * v1.8.1: Fix async register() — OpenClaw requires synchronous registration.
18
+ * Fallback tools registered sync; MCP connection deferred to service.start().
19
+ *
20
+ * v1.9.0: Backward-compat shim tools (memory_search, memory_get) to prevent
21
+ * "allowList contains unknown entries" warnings when NM replaces memory-core.
22
+ *
23
+ * v1.10.0: Singleton MCP client — multiple workspaces (multi-agent) share
24
+ * the same connected client instance, keyed by (pythonPath, brain). Fixes
25
+ * "NeuralMemory service not running" when OpenClaw registers the plugin
26
+ * for a second workspace after gateway startup.
27
+ *
28
+ * Registers:
29
+ * N tools — dynamically from MCP server (fallback: 5 core + 2 compat)
30
+ * 1 service — MCP process lifecycle (start/stop)
31
+ * 6 hooks — before_agent_start (auto-context), agent_end (auto-capture),
32
+ * session:compact:before (flush), command:new/reset (flush),
33
+ * gateway:startup (consolidation)
34
+ */
35
+
36
+ import type {
37
+ OpenClawPluginDefinition,
38
+ OpenClawPluginApi,
39
+ BeforeAgentStartEvent,
40
+ BeforeAgentStartResult,
41
+ AgentEndEvent,
42
+ SessionCompactEvent,
43
+ CommandEvent,
44
+ GatewayStartupEvent,
45
+ } from "./types.js";
46
+ import { NeuralMemoryMcpClient } from "./mcp-client.js";
47
+ import type { PluginLogger } from "./types.js";
48
+ import { createToolsFromMcp, createFallbackTools, createCompatibilityTools } from "./tools.js";
49
+ import type { ToolDefinition } from "./tools.js";
50
+
51
+ // ── Prompt metadata stripping ─────────────────────────────
52
+
53
+ /**
54
+ * Strip metadata preamble from raw prompts before recall.
55
+ *
56
+ * OpenClaw + Telegram injects JSON metadata, NeuralMemory context blocks,
57
+ * env vars, and system boilerplate into ev.prompt. Passing these raw to
58
+ * nmem_recall creates junk neurons like "[concept] json message id".
59
+ *
60
+ * Stripping order matters — later passes clean up residue from earlier ones.
61
+ */
62
+ export function stripPromptMetadata(raw: string): string {
63
+ let cleaned = raw;
64
+
65
+ // 1. Remove JSON blocks (Telegram metadata, conversation info)
66
+ cleaned = cleaned.replace(
67
+ /^\{[\s\S]*?"(?:conversation|message_id|sender_id|sender|chat_id|update_id)"[\s\S]*?\}$/gm,
68
+ "",
69
+ );
70
+
71
+ // 2. Remove NeuralMemory context sections (## Relevant Memories, etc.)
72
+ // The |$ ensures sections at end-of-string are also stripped.
73
+ cleaned = cleaned.replace(
74
+ /^#{1,3}\s*(?:Relevant Memories|Related Information|Relevant Context|Neural Memory)[\s\S]*?(?=\n#{1,3}\s|\n\n(?![-•*\s])|$)/gim,
75
+ "",
76
+ );
77
+
78
+ // 3. Remove neuron-type bullet lines injected by NM context
79
+ cleaned = cleaned.replace(
80
+ /^-\s*\[(?:concept|entity|decision|error|preference|insight|memory|fact|workflow|instruction|pattern)\].*$/gim,
81
+ "",
82
+ );
83
+
84
+ // 4. Remove [NeuralMemory — ...] wrapper lines
85
+ cleaned = cleaned.replace(/^\[NeuralMemory\s*[—–-].*\]$/gm, "");
86
+
87
+ // 5. Remove metadata labels (untrusted metadata lines)
88
+ cleaned = cleaned.replace(
89
+ /^(?:Conversation info|Sender|Context|System)\s*\(.*?\)\s*:?\s*$/gim,
90
+ "",
91
+ );
92
+
93
+ // 6. Remove env/export lines
94
+ cleaned = cleaned.replace(/^export\s+\w+=.*$/gm, "");
95
+
96
+ // 7. Collapse whitespace runs
97
+ cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
98
+
99
+ // Fallback: if everything was stripped, use last non-empty line of raw
100
+ if (!cleaned) {
101
+ const lines = raw.split("\n").filter((l) => l.trim());
102
+ cleaned = lines[lines.length - 1]?.trim() ?? raw.trim();
103
+ }
104
+
105
+ return cleaned;
106
+ }
107
+
108
+ // ── System prompt for tool awareness ──────────────────────
109
+
110
+ /**
111
+ * Build a system prompt listing all registered tool names.
112
+ * This makes the agent aware of which nmem_* tools are available.
113
+ */
114
+ function buildToolInstructions(tools: ToolDefinition[]): string {
115
+ const toolList = tools
116
+ .map((t) => `- ${t.name}: ${t.description.slice(0, 100)}`)
117
+ .join("\n");
118
+
119
+ return `Neural Memory gives you persistent memory across sessions. Use it proactively — each session starts fresh, so without explicit saves ALL discoveries are lost.
120
+
121
+ These are TOOL CALLS, not CLI commands. Do NOT run "nmem remember" in terminal.
122
+
123
+ ## Available Tools
124
+ ${toolList}
125
+
126
+ nmem_* is your primary memory system. memory_search/memory_get are legacy aliases for nmem_recall.
127
+
128
+ ## WHEN TO RECALL
129
+ - New session starts → nmem_recall("current project context")
130
+ - User references past event → nmem_recall("<that topic>")
131
+ - Prefix queries with project name for precision
132
+
133
+ ## WHEN TO SAVE
134
+ After each task: did you make a decision (type="decision", priority=7), fix a bug (type="error", priority=7), learn a preference (type="preference", priority=8), or discover an insight (type="insight", priority=6)?
135
+
136
+ Save with: nmem_remember(content="Chose X over Y because Z", type="decision", priority=7, tags=["project", "topic"])
137
+
138
+ ## CONTENT QUALITY
139
+ - Max 1-3 sentences. Use causal language: "Chose X because Y", "Root cause was X, fixed by Y".
140
+ - Always include project name + topic in tags (lowercase).
141
+ - For temporary scratch notes: nmem_remember(content="...", ephemeral=true) — auto-expires, never synced.
142
+
143
+ ## SESSION END
144
+ nmem_auto(action="process", text="<brief session summary>")
145
+
146
+ ## COMPACT MODE
147
+ All tools support compact=true (saves 60-80% tokens) and token_budget=N.`;
148
+ }
149
+
150
+ // ── Config ─────────────────────────────────────────────────
151
+
152
+ type PluginConfig = {
153
+ pythonPath: string;
154
+ brain: string;
155
+ autoContext: boolean;
156
+ autoCapture: boolean;
157
+ autoFlush: boolean;
158
+ autoConsolidate: boolean;
159
+ contextDepth: number;
160
+ maxContextTokens: number;
161
+ timeout: number;
162
+ initTimeout: number;
163
+ };
164
+
165
+ const DEFAULT_CONFIG: Readonly<PluginConfig> = {
166
+ pythonPath: "python",
167
+ brain: "default",
168
+ autoContext: true,
169
+ autoCapture: true,
170
+ autoFlush: true,
171
+ autoConsolidate: true,
172
+ contextDepth: 1,
173
+ maxContextTokens: 500,
174
+ timeout: 30_000,
175
+ initTimeout: 90_000,
176
+ };
177
+
178
+ export const BRAIN_NAME_RE = /^[a-zA-Z0-9_\-.]{1,64}$/;
179
+ export const MAX_AUTO_CAPTURE_CHARS = 50_000;
180
+
181
+ export function resolveConfig(raw?: Record<string, unknown>): PluginConfig {
182
+ const merged = { ...DEFAULT_CONFIG, ...(raw ?? {}) };
183
+
184
+ return {
185
+ pythonPath:
186
+ typeof merged.pythonPath === "string" && merged.pythonPath.length > 0
187
+ ? merged.pythonPath
188
+ : DEFAULT_CONFIG.pythonPath,
189
+ brain:
190
+ typeof merged.brain === "string" && BRAIN_NAME_RE.test(merged.brain)
191
+ ? merged.brain
192
+ : DEFAULT_CONFIG.brain,
193
+ autoContext:
194
+ typeof merged.autoContext === "boolean"
195
+ ? merged.autoContext
196
+ : DEFAULT_CONFIG.autoContext,
197
+ autoCapture:
198
+ typeof merged.autoCapture === "boolean"
199
+ ? merged.autoCapture
200
+ : DEFAULT_CONFIG.autoCapture,
201
+ autoFlush:
202
+ typeof merged.autoFlush === "boolean"
203
+ ? merged.autoFlush
204
+ : DEFAULT_CONFIG.autoFlush,
205
+ autoConsolidate:
206
+ typeof merged.autoConsolidate === "boolean"
207
+ ? merged.autoConsolidate
208
+ : DEFAULT_CONFIG.autoConsolidate,
209
+ contextDepth:
210
+ typeof merged.contextDepth === "number" &&
211
+ Number.isInteger(merged.contextDepth) &&
212
+ merged.contextDepth >= 0 &&
213
+ merged.contextDepth <= 3
214
+ ? merged.contextDepth
215
+ : DEFAULT_CONFIG.contextDepth,
216
+ maxContextTokens:
217
+ typeof merged.maxContextTokens === "number" &&
218
+ Number.isInteger(merged.maxContextTokens) &&
219
+ merged.maxContextTokens >= 100 &&
220
+ merged.maxContextTokens <= 10_000
221
+ ? merged.maxContextTokens
222
+ : DEFAULT_CONFIG.maxContextTokens,
223
+ timeout:
224
+ typeof merged.timeout === "number" &&
225
+ Number.isFinite(merged.timeout) &&
226
+ merged.timeout >= 5_000 &&
227
+ merged.timeout <= 120_000
228
+ ? merged.timeout
229
+ : DEFAULT_CONFIG.timeout,
230
+ initTimeout:
231
+ typeof merged.initTimeout === "number" &&
232
+ Number.isFinite(merged.initTimeout) &&
233
+ merged.initTimeout >= 10_000 &&
234
+ merged.initTimeout <= 300_000
235
+ ? merged.initTimeout
236
+ : DEFAULT_CONFIG.initTimeout,
237
+ };
238
+ }
239
+
240
+ // ── Singleton MCP client pool ────────────────────────────────
241
+ // Multiple workspaces may call register() independently, but all
242
+ // should share the same MCP process per (pythonPath, brain) combo.
243
+
244
+ const mcpClients = new Map<string, NeuralMemoryMcpClient>();
245
+
246
+ function getOrCreateMcpClient(
247
+ cfg: PluginConfig,
248
+ logger: PluginLogger,
249
+ ): NeuralMemoryMcpClient {
250
+ const key = `${cfg.pythonPath}::${cfg.brain}`;
251
+
252
+ const existing = mcpClients.get(key);
253
+ if (existing) {
254
+ logger.debug?.(`Reusing existing MCP client for brain "${cfg.brain}"`);
255
+ return existing;
256
+ }
257
+
258
+ const mcp = new NeuralMemoryMcpClient({
259
+ pythonPath: cfg.pythonPath,
260
+ brain: cfg.brain,
261
+ logger,
262
+ timeout: cfg.timeout,
263
+ initTimeout: cfg.initTimeout,
264
+ });
265
+
266
+ mcpClients.set(key, mcp);
267
+ return mcp;
268
+ }
269
+
270
+ // ── Plugin definition ──────────────────────────────────────
271
+
272
+ const plugin: OpenClawPluginDefinition = {
273
+ id: "neuralmemory",
274
+ name: "NeuralMemory",
275
+ description:
276
+ "Brain-inspired persistent memory for AI agents neurons, synapses, and fibers",
277
+ version: "1.15.0",
278
+ kind: "memory",
279
+
280
+ register(api: OpenClawPluginApi): void {
281
+ const cfg = resolveConfig(api.pluginConfig);
282
+
283
+ const mcp = getOrCreateMcpClient(cfg, api.logger);
284
+
285
+ // ── Register fallback tools synchronously ────────────
286
+ // OpenClaw requires register() to be synchronous.
287
+ // Register stable fallback tools immediately; MCP connection
288
+ // and dynamic tool discovery happen in service.start().
289
+ // Fallback tools auto-reconnect MCP on first call.
290
+
291
+ const registeredTools = createFallbackTools(mcp);
292
+ const compatTools = createCompatibilityTools(mcp);
293
+
294
+ for (const t of [...registeredTools, ...compatTools]) {
295
+ api.registerTool(t, { name: t.name });
296
+ }
297
+
298
+ api.logger.info(
299
+ `Registered ${registeredTools.length} NeuralMemory tools + ${compatTools.length} compat shims (sync)`,
300
+ );
301
+
302
+ // ── Service: MCP process lifecycle ───────────────────
303
+
304
+ api.registerService({
305
+ id: "neuralmemory-mcp",
306
+
307
+ async start(): Promise<void> {
308
+ if (!mcp.connected) {
309
+ try {
310
+ await mcp.connect();
311
+ api.logger.info("NeuralMemory MCP connected in service.start()");
312
+
313
+ // Log discovered tools for diagnostics (cannot re-register
314
+ // after register() — OpenClaw freezes the tool list).
315
+ try {
316
+ const dynamicTools = await createToolsFromMcp(mcp);
317
+ api.logger.info(
318
+ `NeuralMemory MCP discovered ${dynamicTools.length} tools`,
319
+ );
320
+ } catch (err) {
321
+ api.logger.warn(
322
+ `Tool discovery failed: ${(err as Error).message}`,
323
+ );
324
+ }
325
+ } catch (err) {
326
+ api.logger.error(
327
+ `Failed to start NeuralMemory MCP: ${(err as Error).message}`,
328
+ );
329
+ throw err;
330
+ }
331
+ }
332
+ },
333
+
334
+ async stop(): Promise<void> {
335
+ // Remove from singleton pool so next register() creates fresh client
336
+ const key = `${cfg.pythonPath}::${cfg.brain}`;
337
+ mcpClients.delete(key);
338
+ await mcp.close();
339
+ api.logger.info("NeuralMemory MCP service stopped");
340
+ },
341
+ });
342
+
343
+ // ── Hook: tool awareness + auto-context before agent start ───
344
+
345
+ api.on(
346
+ "before_agent_start",
347
+ async (
348
+ event: unknown,
349
+ _ctx: unknown,
350
+ ): Promise<BeforeAgentStartResult | void> => {
351
+ const result: BeforeAgentStartResult = {
352
+ systemPrompt: buildToolInstructions(registeredTools),
353
+ };
354
+
355
+ if (cfg.autoContext && mcp.connected) {
356
+ const ev = event as BeforeAgentStartEvent;
357
+
358
+ try {
359
+ const query = stripPromptMetadata(ev.prompt);
360
+ const raw = await mcp.callTool("nmem_recall", {
361
+ query,
362
+ depth: cfg.contextDepth,
363
+ max_tokens: cfg.maxContextTokens,
364
+ });
365
+
366
+ const data = JSON.parse(raw) as {
367
+ answer?: string;
368
+ confidence?: number;
369
+ };
370
+
371
+ if (data.answer && (data.confidence ?? 0) > 0.1) {
372
+ result.prependContext = `[NeuralMemory — relevant context]\n${data.answer}`;
373
+ }
374
+ } catch (err) {
375
+ api.logger.warn(
376
+ `Auto-context failed: ${(err as Error).message}`,
377
+ );
378
+ }
379
+ }
380
+
381
+ return result;
382
+ },
383
+ { priority: 10 },
384
+ );
385
+
386
+ // ── Hook: auto-capture after agent completes ────────
387
+
388
+ if (cfg.autoCapture) {
389
+ api.on(
390
+ "agent_end",
391
+ async (event: unknown, _ctx: unknown): Promise<void> => {
392
+ if (!mcp.connected) return;
393
+
394
+ const ev = event as AgentEndEvent;
395
+ if (!ev.success) return;
396
+
397
+ try {
398
+ const messages = ev.messages?.slice(-5) ?? [];
399
+ const text = messages
400
+ .filter(
401
+ (m: unknown): m is { role: string; content: string } =>
402
+ typeof m === "object" &&
403
+ m !== null &&
404
+ (m as { role?: string }).role === "assistant" &&
405
+ typeof (m as { content?: unknown }).content === "string",
406
+ )
407
+ .map((m) => m.content)
408
+ .join("\n")
409
+ .slice(0, MAX_AUTO_CAPTURE_CHARS);
410
+
411
+ if (text.length > 50) {
412
+ await mcp.callTool("nmem_auto", {
413
+ action: "process",
414
+ text,
415
+ });
416
+ }
417
+ } catch (err) {
418
+ api.logger.warn(
419
+ `Auto-capture failed: ${(err as Error).message}`,
420
+ );
421
+ }
422
+ },
423
+ { priority: 90 },
424
+ );
425
+ }
426
+
427
+ // ── Hook: flush memories before context compaction ──
428
+
429
+ if (cfg.autoFlush) {
430
+ api.on(
431
+ "session:compact:before",
432
+ async (_event: unknown, _ctx: unknown): Promise<void> => {
433
+ if (!mcp.connected) return;
434
+
435
+ try {
436
+ await mcp.callTool("nmem_auto", {
437
+ action: "process",
438
+ text: "[pre-compact emergency flush]",
439
+ });
440
+ api.logger.info("Pre-compact flush completed");
441
+ } catch (err) {
442
+ api.logger.warn(
443
+ `Pre-compact flush failed: ${(err as Error).message}`,
444
+ );
445
+ }
446
+ },
447
+ { priority: 5 },
448
+ );
449
+
450
+ // Flush on session boundary (new/reset commands)
451
+ api.on(
452
+ "command:new",
453
+ async (_event: unknown, _ctx: unknown): Promise<void> => {
454
+ if (!mcp.connected) return;
455
+
456
+ try {
457
+ await mcp.callTool("nmem_auto", {
458
+ action: "process",
459
+ text: "[session boundary — command:new]",
460
+ });
461
+ } catch (err) {
462
+ api.logger.warn(
463
+ `Session boundary flush failed: ${(err as Error).message}`,
464
+ );
465
+ }
466
+ },
467
+ { priority: 10 },
468
+ );
469
+
470
+ api.on(
471
+ "command:reset",
472
+ async (_event: unknown, _ctx: unknown): Promise<void> => {
473
+ if (!mcp.connected) return;
474
+
475
+ try {
476
+ await mcp.callTool("nmem_auto", {
477
+ action: "process",
478
+ text: "[session boundary — command:reset]",
479
+ });
480
+ } catch (err) {
481
+ api.logger.warn(
482
+ `Session reset flush failed: ${(err as Error).message}`,
483
+ );
484
+ }
485
+ },
486
+ { priority: 10 },
487
+ );
488
+ }
489
+
490
+ // ── Hook: consolidation on gateway startup ───────────
491
+
492
+ if (cfg.autoConsolidate) {
493
+ api.on(
494
+ "gateway:startup",
495
+ async (_event: unknown, _ctx: unknown): Promise<void> => {
496
+ if (!mcp.connected) {
497
+ try {
498
+ await mcp.ensureConnected();
499
+ } catch (err) {
500
+ api.logger.warn(
501
+ `MCP connect on startup failed: ${(err as Error).message}`,
502
+ );
503
+ return;
504
+ }
505
+ }
506
+
507
+ try {
508
+ await mcp.callTool("nmem_consolidate", {
509
+ strategy: "enrich",
510
+ compact: true,
511
+ });
512
+ api.logger.info("Startup consolidation completed");
513
+ } catch (err) {
514
+ api.logger.warn(
515
+ `Startup consolidation failed: ${(err as Error).message}`,
516
+ );
517
+ }
518
+ },
519
+ { priority: 50 },
520
+ );
521
+ }
522
+
523
+ // ── Done ────────────────────────────────────────────
524
+
525
+ api.logger.info(
526
+ `NeuralMemory registered (brain: ${cfg.brain}, ` +
527
+ `autoContext: ${cfg.autoContext}, autoCapture: ${cfg.autoCapture}, ` +
528
+ `autoFlush: ${cfg.autoFlush}, autoConsolidate: ${cfg.autoConsolidate}) — ` +
529
+ `tools will be loaded dynamically from MCP on service start`,
530
+ );
531
+ },
532
+ };
533
+
534
+ export default plugin;
package/src/types.ts CHANGED
@@ -50,6 +50,22 @@ export type AgentContext = {
50
50
  workspaceDir?: string;
51
51
  };
52
52
 
53
+ export type SessionCompactEvent = {
54
+ reason?: string;
55
+ messagesBeforeCompact?: number;
56
+ };
57
+
58
+ export type CommandEvent = {
59
+ command: string; // "new", "reset", "stop"
60
+ sessionKey?: string;
61
+ workspaceDir?: string;
62
+ };
63
+
64
+ export type GatewayStartupEvent = {
65
+ version?: string;
66
+ workspaceDir?: string;
67
+ };
68
+
53
69
  export type OpenClawPluginApi = {
54
70
  id: string;
55
71
  name: string;