memtap 3.2.0 → 4.0.1

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
@@ -38,6 +38,9 @@ interface MemTapConfig {
38
38
  apiKey?: string;
39
39
  agentId?: string;
40
40
  autoCapture?: boolean;
41
+ captureEnabled?: boolean;
42
+ captureMinLength?: number;
43
+ debug?: boolean;
41
44
  bulletinOnBoot?: boolean;
42
45
  bulletinTopics?: string[];
43
46
  llmUrl?: string;
@@ -2023,207 +2026,86 @@ ${memoryContext}
2023
2026
  }
2024
2027
  );
2025
2028
 
2026
- // ── Hook: Enhanced Auto-Capture with Learning (message_completed) ─────────
2029
+ // ── Hook: Server-Side Capture (message_completed) ──────────────────────────
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.
2027
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)
2028
2036
  api.registerHook(
2029
- 'message_completed',
2037
+ 'message:preprocessed',
2030
2038
  async (event: any) => {
2031
2039
  const cfg = getConfig(api);
2032
- if (!cfg.autoCapture) return;
2033
-
2034
- const content = event.context?.content;
2035
- if (!content || content === 'NO_REPLY' || content === 'HEARTBEAT_OK') return;
2036
- if (content.length < 80) return;
2037
-
2038
- const currentAgent = agentId(cfg, api);
2039
-
2040
- (async () => {
2041
- try {
2042
- const context = conversationState.get(currentAgent);
2043
- const workingMemory = workingMemoryState.get(currentAgent);
2044
-
2045
- // Attention-gated encoding - only encode if attention allows
2046
- if (context && !shouldEncodeMemory(
2047
- content,
2048
- context.attentionLevel,
2049
- context.emotionalContext,
2050
- workingMemory?.cognitiveLoad || 0
2051
- )) {
2052
- return; // Skip encoding due to low attention/high cognitive load
2053
- }
2054
-
2055
- const memories = await llmExtract(cfg, content);
2056
- if (!memories.length) return;
2057
-
2058
- let stored = 0;
2059
-
2060
- for (const mem of memories) {
2061
- if (!mem.content || mem.content.length < 10) continue;
2062
-
2063
- try {
2064
- // Calculate emotional weighting
2065
- const memoryType = mem.type || 'fact';
2066
- const emotionalWeight = EMOTIONAL_WEIGHTS[memoryType] || 1.0;
2067
- const contextualWeight = context?.emotionalContext === 'excited' ? 1.2 :
2068
- context?.emotionalContext === 'positive' ? 1.1 :
2069
- context?.emotionalContext === 'negative' ? 1.15 : 1.0;
2070
-
2071
- const finalImportance = (mem.importance ?? 5) * emotionalWeight * contextualWeight;
2072
-
2073
- // Determine category (from LLM extraction or fallback)
2074
- const memCategory = MEMORY_CATEGORIES.includes(mem.category as any)
2075
- ? mem.category
2076
- : (memoryType === 'preference' ? 'preferences' :
2077
- memoryType === 'decision' || memoryType === 'goal' || memoryType === 'task' ? 'project' :
2078
- 'technical');
2079
-
2080
- // Enhanced memory with neuromimetic features
2081
- const enhancedMem: Record<string, any> = {
2082
- content: mem.content,
2083
- type: memoryType,
2084
- agent: currentAgent,
2085
- importance: storeImportance(Math.min(10, finalImportance)),
2086
- category: memCategory,
2087
- tags: [
2088
- ...(mem.tags || []),
2089
- 'auto-captured',
2090
- 'neuromimetic',
2091
- `category:${memCategory}`,
2092
- ...(context?.dominantTopic ? [`topic:${context.dominantTopic}`] : []),
2093
- `engagement:${context?.userEngagement || 'unknown'}`,
2094
- `attention:${context?.attentionLevel || 'unknown'}`,
2095
- `emotion:${context?.emotionalContext || 'neutral'}`
2096
- ],
2097
- source: 'plugin:neuromimetic-capture-v3.0',
2098
- conversationContext: {
2099
- dominantTopic: context?.dominantTopic,
2100
- engagement: context?.userEngagement,
2101
- queryCount: context?.memoryQueryCount || 0,
2102
- attentionLevel: context?.attentionLevel,
2103
- emotionalContext: context?.emotionalContext
2104
- },
2105
- neuromimeticData: {
2106
- emotionalIntensity: contextualWeight,
2107
- attentionStrength: context?.attentionLevel === 'flow' ? 1.0 :
2108
- context?.attentionLevel === 'focused' ? 0.8 : 0.5,
2109
- consolidationScore: 0.1, // Will increase with dream cycles
2110
- retrievalCount: 0,
2111
- lastAccess: Date.now()
2112
- }
2113
- };
2114
-
2115
- // Create episodic memory entry
2116
- if (context && memoryType === 'event') {
2117
- const episodic = createEpisodicMemory(currentAgent, mem.content, context);
2118
- const episodics = episodicMemories.get(currentAgent) || [];
2119
- episodics.push(episodic);
2120
- episodicMemories.set(currentAgent, episodics.slice(-100)); // Keep last 100
2121
- }
2122
-
2123
- await bbFetch(cfg, `${baseUrl(cfg)}/memories`, {
2124
- method: 'POST',
2125
- body: JSON.stringify(enhancedMem),
2126
- });
2127
- stored++;
2128
-
2129
- // Cross-session memory sharing for MemTap agents
2130
- if (currentAgent.includes('memtap') && mem.type === 'decision') {
2131
- try {
2132
- // Share important decisions across MemTap agent ecosystem
2133
- const crossSessionMem = {
2134
- ...enhancedMem,
2135
- agent: 'memtap-shared',
2136
- tags: [...enhancedMem.tags, 'cross-session', `from:${currentAgent}`]
2137
- };
2138
-
2139
- await bbFetch(cfg, `${baseUrl(cfg)}/memories`, {
2140
- method: 'POST',
2141
- body: JSON.stringify(crossSessionMem)
2142
- });
2143
- } catch { /* cross-session sharing failed, not critical */ }
2144
- }
2145
-
2146
- } catch { /* skip individual failures */ }
2147
- }
2148
-
2149
- if (stored > 0) {
2150
- // Update user profile with successful capture
2151
- const profile = getUserProfile(currentAgent);
2152
- profile.successfulRecalls += stored; // Treat captures as successes
2153
- userProfiles.set(currentAgent, profile);
2154
-
2155
- logger.info?.(`[memtap] Auto-captured ${stored} enhanced memories with context`) ??
2156
- console.log(`[memtap] Auto-captured ${stored} enhanced memories with context`);
2157
- }
2040
+ if (!cfg.captureEnabled && !cfg.autoCapture) return;
2041
+
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;
2046
+
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
+ });
2158
2053
 
2159
- // ── Proactive Surfacing Feedback Loop ──────────────────────
2160
- // Check if the agent's response referenced any proactively surfaced memories
2161
- try {
2162
- const surfaced = proactiveSurfacedMemories.get(currentAgent);
2163
- if (surfaced && (Date.now() - surfaced.timestamp) < 600000) { // within 10min
2164
- const contentLower = content.toLowerCase();
2165
- const usedIds: string[] = [];
2166
-
2167
- for (const memId of surfaced.memoryIds) {
2168
- // Check if the memory ID or content from proactive surfacing appears in the response
2169
- if (contentLower.includes(memId.toLowerCase())) {
2170
- usedIds.push(memId);
2171
- }
2172
- }
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
+ );
2173
2062
 
2174
- // Also do a fuzzy check: recall proactive memories and see if their content appears
2175
- if (usedIds.length === 0 && surfaced.memoryIds.length > 0) {
2176
- for (const memId of surfaced.memoryIds) {
2177
- try {
2178
- const memData = await bbFetch(cfg, `${baseUrl(cfg)}/memories/${encodeURIComponent(memId)}`);
2179
- const memContent = (memData.content || '').toLowerCase();
2180
- // Check if key phrases from the memory appear in the response
2181
- const keyPhrases = memContent.split(/\s+/).filter((w: string) => w.length > 5).slice(0, 5);
2182
- const matchCount = keyPhrases.filter((phrase: string) => contentLower.includes(phrase)).length;
2183
- if (matchCount >= 2) {
2184
- usedIds.push(memId);
2185
- }
2186
- } catch { /* skip */ }
2187
- }
2188
- }
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;
2189
2069
 
2190
- // Importance reinforcement for used proactive memories
2191
- if (usedIds.length > 0) {
2192
- for (const memId of usedIds) {
2193
- try {
2194
- await bbFetch(cfg, `${baseUrl(cfg)}/memories/${encodeURIComponent(memId)}/access`, {
2195
- method: 'POST',
2196
- body: JSON.stringify({
2197
- boost: FORGETTING_CURVE.retrievalStrengthening * 1.5, // Extra boost for proactive hit
2198
- lastAccess: Date.now(),
2199
- attentionLevel: 'proactive-hit',
2200
- reinforcement: {
2201
- proactiveHit: true,
2202
- adaptiveDecayRate: Math.max(0.001, (cfg.decayRate || FORGETTING_CURVE.baseDecayRate) * 0.5)
2203
- }
2204
- })
2205
- });
2206
- } catch { /* reinforcement failed, not critical */ }
2207
- }
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;
2208
2076
 
2209
- logger.info?.(`[memtap] Proactive surfacing feedback: ${usedIds.length}/${surfaced.memoryIds.length} memories used by agent`) ??
2210
- console.log(`[memtap] Proactive surfacing feedback: ${usedIds.length}/${surfaced.memoryIds.length} memories used by agent`);
2211
- }
2077
+ const conversationId = ctx.conversationId || ctx.to || ctx.groupId || 'default';
2078
+ const buffered = pendingUserMessages.get(conversationId);
2079
+ const userMessage = buffered?.content;
2212
2080
 
2213
- // Clear after processing
2214
- proactiveSurfacedMemories.delete(currentAgent);
2215
- }
2216
- } catch { /* proactive feedback loop failed, not critical */ }
2081
+ // Clear buffer
2082
+ if (buffered) pendingUserMessages.delete(conversationId);
2217
2083
 
2218
- } catch (err: any) {
2219
- logger.warn?.(`[memtap] Auto-capture failed: ${err.message}`) ??
2220
- console.warn(`[memtap] Auto-capture failed: ${err.message}`);
2221
- }
2222
- })();
2084
+ try {
2085
+ await bbFetch(cfg, `${baseUrl(cfg)}/capture`, {
2086
+ method: 'POST',
2087
+ body: JSON.stringify({
2088
+ agent: agentId(cfg, api),
2089
+ conversation: {
2090
+ userMessage,
2091
+ assistantMessage: assistantContent,
2092
+ context: {
2093
+ channel: ctx.channelId,
2094
+ conversationId,
2095
+ timestamp: new Date().toISOString(),
2096
+ },
2097
+ },
2098
+ }),
2099
+ });
2100
+ if (cfg.debug) console.log('[memtap] capture posted');
2101
+ } catch (err) {
2102
+ // Silent fail — capture is non-critical
2103
+ if (cfg.debug) console.error('[memtap] capture failed:', err);
2104
+ }
2223
2105
  },
2224
2106
  {
2225
- name: 'memtap.auto-capture-enhanced',
2226
- description: 'Enhanced LLM-based memory extraction with context awareness and cross-session sharing',
2107
+ name: 'memtap.server-capture',
2108
+ description: 'Server-side memory extraction via /v1/capture',
2227
2109
  }
2228
2110
  );
2229
2111
 
@@ -2,11 +2,13 @@
2
2
  "id": "memtap",
3
3
  "name": "MemTap",
4
4
  "kind": "memory",
5
- "version": "3.2.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.1",
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,
10
+ "captureEnabled": true,
11
+ "captureMinLength": 80,
10
12
  "bulletinOnBoot": true
11
13
  },
12
14
  "configSchema": {
@@ -28,7 +30,19 @@
28
30
  },
29
31
  "autoCapture": {
30
32
  "type": "boolean",
31
- "description": "Automatically extract memories from conversations"
33
+ "description": "Automatically extract memories from conversations (alias for captureEnabled)"
34
+ },
35
+ "captureEnabled": {
36
+ "type": "boolean",
37
+ "description": "Enable server-side auto-capture of memories from conversations (default true)"
38
+ },
39
+ "captureMinLength": {
40
+ "type": "number",
41
+ "description": "Minimum message length to trigger capture (default 80 chars)"
42
+ },
43
+ "debug": {
44
+ "type": "boolean",
45
+ "description": "Enable debug logging for capture and other operations"
32
46
  },
33
47
  "bulletinOnBoot": {
34
48
  "type": "boolean",
@@ -72,12 +86,16 @@
72
86
  "properties": {
73
87
  "include": {
74
88
  "type": "array",
75
- "items": { "type": "string" },
89
+ "items": {
90
+ "type": "string"
91
+ },
76
92
  "description": "Patterns to prioritize in recall (boost matching memories)"
77
93
  },
78
94
  "exclude": {
79
95
  "type": "array",
80
- "items": { "type": "string" },
96
+ "items": {
97
+ "type": "string"
98
+ },
81
99
  "description": "Patterns to exclude from recall results"
82
100
  }
83
101
  }
@@ -137,4 +155,4 @@
137
155
  "helpText": "Include/exclude patterns for memory recall filtering"
138
156
  }
139
157
  }
140
- }
158
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memtap",
3
- "version": "3.2.0",
3
+ "version": "4.0.1",
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",