memtap 4.0.0 → 4.0.2

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/index.ts CHANGED
@@ -2027,19 +2027,59 @@ ${memoryContext}
2027
2027
  );
2028
2028
 
2029
2029
  // ── Hook: Server-Side Capture (message_completed) ──────────────────────────
2030
- // v4.0.0: Extraction moved to server. Plugin just POSTs conversation to /capture.
2030
+ // v4.0.1: Extraction moved to server. Plugin posts user+assistant conversations to /capture.
2031
+ // We buffer user messages per conversation, then capture the pair when the assistant replies.
2031
2032
 
2033
+ const pendingUserMessages = new Map<string, { content: string; channel?: string; timestamp: string }>();
2034
+
2035
+ // Capture user's inbound message (store for pairing with assistant reply)
2032
2036
  api.registerHook(
2033
- 'message_completed',
2037
+ 'message_received',
2034
2038
  async (event: any) => {
2035
2039
  const cfg = getConfig(api);
2036
2040
  if (!cfg.captureEnabled && !cfg.autoCapture) return;
2037
2041
 
2038
- const content = event.context?.content;
2039
- if (!content || content === 'NO_REPLY' || content === 'HEARTBEAT_OK') return;
2040
- if (content.length < (cfg.captureMinLength || 80)) return;
2042
+ const ctx = event || event?.context || {};
2043
+ const content = ctx.content || ctx.bodyForAgent || ctx.body || '';
2044
+ if (!content || typeof content !== 'string') return;
2045
+ if (content === 'NO_REPLY' || content === 'HEARTBEAT_OK') return;
2041
2046
 
2042
- const userMessage = event.context?.userMessage || event.context?.input;
2047
+ const conversationId = ctx.conversationId || ctx.from || ctx.to || 'default';
2048
+ pendingUserMessages.set(conversationId, {
2049
+ content,
2050
+ channel: ctx.channelId || ctx.provider,
2051
+ timestamp: new Date().toISOString(),
2052
+ });
2053
+
2054
+ // Clean up old pending entries after 10 min
2055
+ setTimeout(() => pendingUserMessages.delete(conversationId), 10 * 60 * 1000);
2056
+ },
2057
+ {
2058
+ name: 'memtap.capture-inbound',
2059
+ description: 'Buffer inbound user messages for capture pairing',
2060
+ }
2061
+ );
2062
+
2063
+ // Capture assistant's outbound reply (pair with buffered user message, then POST to /capture)
2064
+ api.registerHook(
2065
+ 'message_sent',
2066
+ async (event: any) => {
2067
+ const cfg = getConfig(api);
2068
+ if (!cfg.captureEnabled && !cfg.autoCapture) return;
2069
+
2070
+ const ctx = event || event?.context || {};
2071
+ const assistantContent = ctx.content || '';
2072
+ if (!assistantContent || typeof assistantContent !== 'string') return;
2073
+ if (assistantContent === 'NO_REPLY' || assistantContent === 'HEARTBEAT_OK') return;
2074
+ if (assistantContent.length < (cfg.captureMinLength || 80)) return;
2075
+ if (ctx.success === false) return;
2076
+
2077
+ const conversationId = ctx.conversationId || ctx.to || ctx.groupId || 'default';
2078
+ const buffered = pendingUserMessages.get(conversationId);
2079
+ const userMessage = buffered?.content;
2080
+
2081
+ // Clear buffer
2082
+ if (buffered) pendingUserMessages.delete(conversationId);
2043
2083
 
2044
2084
  try {
2045
2085
  await bbFetch(cfg, `${baseUrl(cfg)}/capture`, {
@@ -2048,20 +2088,19 @@ ${memoryContext}
2048
2088
  agent: agentId(cfg, api),
2049
2089
  conversation: {
2050
2090
  userMessage,
2051
- assistantMessage: content,
2091
+ assistantMessage: assistantContent,
2052
2092
  context: {
2053
- channel: event.context?.channel,
2054
- timestamp: new Date().toISOString()
2055
- }
2056
- }
2057
- })
2093
+ channel: ctx.channelId,
2094
+ conversationId,
2095
+ timestamp: new Date().toISOString(),
2096
+ },
2097
+ },
2098
+ }),
2058
2099
  });
2100
+ if (cfg.debug) console.log('[memtap] capture posted');
2059
2101
  } catch (err) {
2060
2102
  // Silent fail — capture is non-critical
2061
- if (cfg.debug) {
2062
- logger.warn?.(`[memtap] capture failed: ${err}`) ??
2063
- console.error('[memtap] capture failed:', err);
2064
- }
2103
+ if (cfg.debug) console.error('[memtap] capture failed:', err);
2065
2104
  }
2066
2105
  },
2067
2106
  {
@@ -2,8 +2,8 @@
2
2
  "id": "memtap",
3
3
  "name": "MemTap",
4
4
  "kind": "memory",
5
- "version": "4.0.0",
6
- "description": "Graph-based long-term memory for OpenClaw agents semantic recall, GraphRAG, entity management, decision tracking, neural auto-capture, anomaly detection, consolidation, profiles, and adaptive decay.",
5
+ "version": "4.0.2",
6
+ "description": "Graph-based long-term memory for OpenClaw agents \u2014 semantic recall, GraphRAG, entity management, decision tracking, neural auto-capture, anomaly detection, consolidation, profiles, and adaptive decay.",
7
7
  "defaultConfig": {
8
8
  "serverUrl": "https://api.memtap.ai",
9
9
  "autoCapture": true,
@@ -86,12 +86,16 @@
86
86
  "properties": {
87
87
  "include": {
88
88
  "type": "array",
89
- "items": { "type": "string" },
89
+ "items": {
90
+ "type": "string"
91
+ },
90
92
  "description": "Patterns to prioritize in recall (boost matching memories)"
91
93
  },
92
94
  "exclude": {
93
95
  "type": "array",
94
- "items": { "type": "string" },
96
+ "items": {
97
+ "type": "string"
98
+ },
95
99
  "description": "Patterns to exclude from recall results"
96
100
  }
97
101
  }
@@ -151,4 +155,4 @@
151
155
  "helpText": "Include/exclude patterns for memory recall filtering"
152
156
  }
153
157
  }
154
- }
158
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memtap",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "description": "MemTap — Graph-based long-term memory plugin for OpenClaw agents. Knowledge graph with semantic recall, GraphRAG, entity management, decision tracking, and auto-capture.",
5
5
  "keywords": [
6
6
  "openclaw",