@jungjaehoon/clawdbot-mama 0.1.2 → 0.2.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/index.ts +154 -6
- package/package.json +3 -1
- package/scripts/postinstall.js +54 -0
package/index.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* NO HTTP/REST - MAMA 로직을 Gateway에 직접 임베드
|
|
5
5
|
* better-sqlite3 + sqlite-vec로 벡터 검색
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - 4 native tools: mama_search, mama_save, mama_load_checkpoint, mama_update
|
|
9
|
+
* - Auto-recall: 에이전트 시작 시 유저 프롬프트 기반 시맨틱 검색
|
|
10
|
+
* - Auto-capture: 에이전트 종료 시 중요 결정 자동 저장
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
13
|
import { Type } from "@sinclair/typebox";
|
|
@@ -53,6 +58,142 @@ const mamaPlugin = {
|
|
|
53
58
|
|
|
54
59
|
register(api: ClawdbotPluginApi) {
|
|
55
60
|
|
|
61
|
+
// =====================================================
|
|
62
|
+
// Auto-recall: 유저 프롬프트 기반 시맨틱 검색
|
|
63
|
+
// =====================================================
|
|
64
|
+
api.on("before_agent_start", async (event: any) => {
|
|
65
|
+
try {
|
|
66
|
+
await initMAMA();
|
|
67
|
+
|
|
68
|
+
const userPrompt = event.prompt || "";
|
|
69
|
+
|
|
70
|
+
// 1. 유저 프롬프트가 있으면 시맨틱 검색 수행
|
|
71
|
+
let semanticResults: any[] = [];
|
|
72
|
+
if (userPrompt && userPrompt.length >= 5) {
|
|
73
|
+
try {
|
|
74
|
+
const searchResult = await mama.suggest(userPrompt, { limit: 3, threshold: 0.5 });
|
|
75
|
+
semanticResults = searchResult?.results || [];
|
|
76
|
+
} catch (searchErr: any) {
|
|
77
|
+
console.error("[MAMA] Semantic search error:", searchErr.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 2. 최근 체크포인트 로드
|
|
82
|
+
const checkpoint = await mama.loadCheckpoint();
|
|
83
|
+
|
|
84
|
+
// 3. 최근 결정들 로드 (시맨틱 검색 결과가 없을 때만)
|
|
85
|
+
let recentDecisions: any[] = [];
|
|
86
|
+
if (semanticResults.length === 0) {
|
|
87
|
+
const recentResult = await mama.list({ limit: 3 });
|
|
88
|
+
recentDecisions = recentResult?.decisions || [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 4. 컨텍스트가 있으면 주입
|
|
92
|
+
if (checkpoint || semanticResults.length > 0 || recentDecisions.length > 0) {
|
|
93
|
+
let content = "<relevant-memories>\n";
|
|
94
|
+
content += "# MAMA Memory Context\n\n";
|
|
95
|
+
|
|
96
|
+
if (semanticResults.length > 0) {
|
|
97
|
+
content += "## Relevant Decisions (semantic match)\n\n";
|
|
98
|
+
semanticResults.forEach((r: any) => {
|
|
99
|
+
const pct = Math.round((r.similarity || 0) * 100);
|
|
100
|
+
content += `- **${r.topic}** [${pct}%]: ${r.decision}`;
|
|
101
|
+
if (r.outcome) content += ` (${r.outcome})`;
|
|
102
|
+
content += `\n _${(r.reasoning || "").substring(0, 100)}..._\n`;
|
|
103
|
+
content += ` ID: \`${r.id}\`\n`;
|
|
104
|
+
});
|
|
105
|
+
content += "\n";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (checkpoint) {
|
|
109
|
+
content += `## Last Checkpoint (${new Date(checkpoint.timestamp).toLocaleString()})\n\n`;
|
|
110
|
+
content += `**Summary:** ${checkpoint.summary}\n\n`;
|
|
111
|
+
if (checkpoint.next_steps) {
|
|
112
|
+
content += `**Next Steps:** ${checkpoint.next_steps}\n\n`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (recentDecisions.length > 0) {
|
|
117
|
+
content += "## Recent Decisions\n\n";
|
|
118
|
+
recentDecisions.forEach((d: any) => {
|
|
119
|
+
content += `- **${d.topic}**: ${d.decision}`;
|
|
120
|
+
if (d.outcome) content += ` (${d.outcome})`;
|
|
121
|
+
content += "\n";
|
|
122
|
+
});
|
|
123
|
+
content += "\n";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
content += "</relevant-memories>";
|
|
127
|
+
|
|
128
|
+
console.log(`[MAMA] Auto-recall: ${semanticResults.length} semantic matches, ${recentDecisions.length} recent, checkpoint: ${!!checkpoint}`);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
prependContext: content,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
} catch (err: any) {
|
|
135
|
+
console.error("[MAMA] Auto-recall error:", err.message);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// =====================================================
|
|
140
|
+
// Auto-capture: 에이전트 종료 시 결정 자동 저장
|
|
141
|
+
// =====================================================
|
|
142
|
+
api.on("agent_end", async (event: any) => {
|
|
143
|
+
if (!event.success || !event.messages || event.messages.length === 0) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
await initMAMA();
|
|
149
|
+
|
|
150
|
+
// 메시지에서 텍스트 추출
|
|
151
|
+
const texts: string[] = [];
|
|
152
|
+
for (const msg of event.messages) {
|
|
153
|
+
if (!msg || typeof msg !== "object") continue;
|
|
154
|
+
|
|
155
|
+
const role = msg.role;
|
|
156
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
157
|
+
|
|
158
|
+
const content = msg.content;
|
|
159
|
+
if (typeof content === "string") {
|
|
160
|
+
texts.push(content);
|
|
161
|
+
} else if (Array.isArray(content)) {
|
|
162
|
+
for (const block of content) {
|
|
163
|
+
if (block?.type === "text" && typeof block.text === "string") {
|
|
164
|
+
texts.push(block.text);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 결정 패턴 감지
|
|
171
|
+
const decisionPatterns = [
|
|
172
|
+
/decided|결정|선택|chose|use.*instead|going with/i,
|
|
173
|
+
/will use|사용할|approach|방식|strategy/i,
|
|
174
|
+
/remember|기억|learned|배웠|lesson/i,
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
for (const text of texts) {
|
|
178
|
+
// Skip short or injected content
|
|
179
|
+
if (text.length < 20 || text.length > 500) continue;
|
|
180
|
+
if (text.includes("<relevant-memories>")) continue;
|
|
181
|
+
if (text.startsWith("<") && text.includes("</")) continue;
|
|
182
|
+
|
|
183
|
+
// Check if it matches decision patterns
|
|
184
|
+
const isDecision = decisionPatterns.some(p => p.test(text));
|
|
185
|
+
if (!isDecision) continue;
|
|
186
|
+
|
|
187
|
+
// Auto-save detected decision (logged only, not actually saved without explicit topic)
|
|
188
|
+
console.log(`[MAMA] Auto-capture candidate: ${text.substring(0, 50)}...`);
|
|
189
|
+
// Note: 실제 저장은 명시적 topic이 필요하므로 로그만 남김
|
|
190
|
+
// 향후 LLM을 통한 topic 추출 기능 추가 가능
|
|
191
|
+
}
|
|
192
|
+
} catch (err: any) {
|
|
193
|
+
console.error("[MAMA] Auto-capture error:", err.message);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
56
197
|
// =====================================================
|
|
57
198
|
// mama_search - 시맨틱 메모리 검색
|
|
58
199
|
// =====================================================
|
|
@@ -60,17 +201,19 @@ const mamaPlugin = {
|
|
|
60
201
|
name: "mama_search",
|
|
61
202
|
description: `Search semantic memory for relevant past decisions.
|
|
62
203
|
|
|
63
|
-
**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
204
|
+
⚠️ **TRIGGERS - Call this BEFORE:**
|
|
205
|
+
• Making architectural choices (check prior art)
|
|
206
|
+
• Calling mama_save (find links first!)
|
|
207
|
+
• Debugging (find past failures on similar issues)
|
|
208
|
+
• Starting work on a topic (load context)
|
|
68
209
|
|
|
69
210
|
**Returns:** Decisions ranked by semantic similarity with:
|
|
70
211
|
- Topic, decision, reasoning
|
|
71
212
|
- Similarity score (0-100%)
|
|
72
213
|
- Decision ID (for linking/updating)
|
|
73
214
|
|
|
215
|
+
**High similarity (>80%) = MUST link with builds_on/debates/synthesizes**
|
|
216
|
+
|
|
74
217
|
**Example queries:** "authentication", "database choice", "error handling"`,
|
|
75
218
|
|
|
76
219
|
parameters: Type.Object({
|
|
@@ -126,6 +269,11 @@ const mamaPlugin = {
|
|
|
126
269
|
name: "mama_save",
|
|
127
270
|
description: `Save a decision or checkpoint to semantic memory.
|
|
128
271
|
|
|
272
|
+
⚠️ **REQUIRED WORKFLOW (Don't create orphans!):**
|
|
273
|
+
1. Call mama_search FIRST to find related decisions
|
|
274
|
+
2. Check if same topic exists (yours will supersede it)
|
|
275
|
+
3. MUST include link in reasoning/summary field
|
|
276
|
+
|
|
129
277
|
**DECISION - Use when:**
|
|
130
278
|
- Making architectural choices
|
|
131
279
|
- Learning a lesson (success or failure)
|
|
@@ -137,7 +285,7 @@ const mamaPlugin = {
|
|
|
137
285
|
- Reaching a milestone
|
|
138
286
|
- Before switching tasks
|
|
139
287
|
|
|
140
|
-
**Link decisions:**
|
|
288
|
+
**Link decisions:** End reasoning with 'builds_on: <id>' or 'debates: <id>' or 'synthesizes: [id1, id2]'`,
|
|
141
289
|
|
|
142
290
|
parameters: Type.Object({
|
|
143
291
|
type: Type.Union([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jungjaehoon/clawdbot-mama",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MAMA Memory Plugin for Clawdbot - Semantic decision memory with reasoning graph",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
+
"postinstall": "node scripts/postinstall.js",
|
|
13
14
|
"test": "echo 'No tests yet'",
|
|
14
15
|
"clean": "rm -rf node_modules"
|
|
15
16
|
},
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"files": [
|
|
43
44
|
"index.ts",
|
|
44
45
|
"clawdbot.plugin.json",
|
|
46
|
+
"scripts/",
|
|
45
47
|
"README.md"
|
|
46
48
|
]
|
|
47
49
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MAMA Clawdbot Plugin - Postinstall Script
|
|
4
|
+
* Downloads embedding model during installation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
console.log('[MAMA] Running postinstall checks...');
|
|
9
|
+
|
|
10
|
+
// Check Node.js version
|
|
11
|
+
const nodeVersion = process.versions.node.split('.')[0];
|
|
12
|
+
if (parseInt(nodeVersion) < 18) {
|
|
13
|
+
console.warn('[MAMA] Warning: Node.js 18+ recommended, current:', process.versions.node);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Try to download embedding model
|
|
17
|
+
console.log('[MAMA] Pre-downloading embedding model...');
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Dynamic import for ESM module
|
|
21
|
+
const { pipeline } = await import('@huggingface/transformers');
|
|
22
|
+
|
|
23
|
+
// This will download and cache the model
|
|
24
|
+
console.log('[MAMA] Downloading Xenova/all-MiniLM-L6-v2 (~30MB)...');
|
|
25
|
+
const extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', {
|
|
26
|
+
quantized: true,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Quick test to verify it works
|
|
30
|
+
const testResult = await extractor('test', { pooling: 'mean', normalize: true });
|
|
31
|
+
console.log('[MAMA] Embedding model ready (dimension:', testResult.data.length, ')');
|
|
32
|
+
} catch (err) {
|
|
33
|
+
// Non-fatal - model will be downloaded on first use
|
|
34
|
+
console.warn('[MAMA] Could not pre-download model:', err.message);
|
|
35
|
+
console.warn('[MAMA] Model will be downloaded on first use.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check better-sqlite3
|
|
39
|
+
try {
|
|
40
|
+
require('better-sqlite3');
|
|
41
|
+
console.log('[MAMA] SQLite native module: OK');
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error('[MAMA] SQLite native module failed:', err.message);
|
|
44
|
+
console.error('[MAMA] You may need build tools: python, make, g++');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log('[MAMA] Postinstall complete.');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
main().catch((err) => {
|
|
51
|
+
console.error('[MAMA] Postinstall error:', err.message);
|
|
52
|
+
// Don't fail installation
|
|
53
|
+
process.exit(0);
|
|
54
|
+
});
|