@stackmemoryai/stackmemory 1.6.0 → 1.6.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/dist/src/cli/commands/orchestrate.js +249 -2
- package/dist/src/cli/commands/orchestrator.js +206 -23
- package/package.json +1 -2
- package/scripts/gepa/.before-optimize.md +0 -112
- package/scripts/gepa/README.md +0 -275
- package/scripts/gepa/config.json +0 -59
- package/scripts/gepa/evals/coding-tasks.jsonl +0 -5
- package/scripts/gepa/evals/fixtures/api-endpoint.ts +0 -31
- package/scripts/gepa/evals/fixtures/brittle-integration.ts +0 -38
- package/scripts/gepa/evals/fixtures/buggy-loop.js +0 -18
- package/scripts/gepa/evals/fixtures/callback-hell.js +0 -53
- package/scripts/gepa/evals/fixtures/fts5-triggers.sql +0 -23
- package/scripts/gepa/evals/fixtures/leaky-service.ts +0 -70
- package/scripts/gepa/evals/fixtures/mcp-dispatch-stub.ts +0 -39
- package/scripts/gepa/evals/fixtures/pr-diff.txt +0 -24
- package/scripts/gepa/evals/fixtures/unsafe-webhook.ts +0 -34
- package/scripts/gepa/evals/fixtures/unwrapped-db-op.ts +0 -42
- package/scripts/gepa/evals/stackmemory-tasks.jsonl +0 -8
- package/scripts/gepa/generations/gen-000/baseline.md +0 -112
- package/scripts/gepa/generations/gen-001/baseline.md +0 -112
- package/scripts/gepa/generations/gen-001/variant-a.md +0 -107
- package/scripts/gepa/generations/gen-001/variant-b.md +0 -216
- package/scripts/gepa/generations/gen-001/variant-c.md +0 -83
- package/scripts/gepa/generations/gen-001/variant-d.md +0 -90
- package/scripts/gepa/hooks/auto-optimize.js +0 -494
- package/scripts/gepa/hooks/eval-tracker.js +0 -203
- package/scripts/gepa/hooks/reflect.js +0 -350
- package/scripts/gepa/optimize.js +0 -853
- package/scripts/gepa/results/eval-1-baseline.json +0 -218
- package/scripts/gepa/results/eval-1-variant-a.json +0 -218
- package/scripts/gepa/results/eval-1-variant-b.json +0 -218
- package/scripts/gepa/results/eval-1-variant-c.json +0 -218
- package/scripts/gepa/results/eval-1-variant-d.json +0 -218
- package/scripts/gepa/state.json +0 -49
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* GEPA Reflection Engine
|
|
4
|
-
*
|
|
5
|
-
* Analyzes evaluation results to generate insights for next mutation cycle.
|
|
6
|
-
* This is the key differentiator from random mutations.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'fs';
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
import { spawn } from 'child_process';
|
|
13
|
-
|
|
14
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
const PROJECT_ROOT = path.resolve(__dirname, '../../..');
|
|
16
|
-
|
|
17
|
-
// Load .env from project root
|
|
18
|
-
const envPath = path.join(PROJECT_ROOT, '.env');
|
|
19
|
-
if (fs.existsSync(envPath)) {
|
|
20
|
-
for (const line of fs.readFileSync(envPath, 'utf8').split('\n')) {
|
|
21
|
-
const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
|
22
|
-
if (match && !process.env[match[1]]) {
|
|
23
|
-
process.env[match[1]] = match[2].replace(/^["']|["']$/g, '');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Call Claude CLI via spawn (stdin pipe, no shell interpolation)
|
|
30
|
-
*/
|
|
31
|
-
function spawnClaude(prompt) {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
const child = spawn('claude', ['--print'], {
|
|
34
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
35
|
-
env: { ...process.env },
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
let stdout = '';
|
|
39
|
-
let stderr = '';
|
|
40
|
-
|
|
41
|
-
child.stdout.on('data', (d) => (stdout += d));
|
|
42
|
-
child.stderr.on('data', (d) => (stderr += d));
|
|
43
|
-
|
|
44
|
-
child.on('close', (code) => {
|
|
45
|
-
if (code !== 0 && !stdout)
|
|
46
|
-
return reject(new Error(stderr || `claude exited ${code}`));
|
|
47
|
-
resolve(stdout);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
child.on('error', reject);
|
|
51
|
-
|
|
52
|
-
child.stdin.write(prompt);
|
|
53
|
-
child.stdin.end();
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const GEPA_DIR = path.join(__dirname, '..');
|
|
58
|
-
const RESULTS_DIR = path.join(GEPA_DIR, 'results');
|
|
59
|
-
const GENERATIONS_DIR = path.join(GEPA_DIR, 'generations');
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Analyze session data for patterns
|
|
63
|
-
*/
|
|
64
|
-
function analyzeSessionPatterns() {
|
|
65
|
-
const sessionsDir = path.join(RESULTS_DIR, 'sessions');
|
|
66
|
-
if (!fs.existsSync(sessionsDir)) return { patterns: [], insights: [] };
|
|
67
|
-
|
|
68
|
-
const sessions = fs
|
|
69
|
-
.readdirSync(sessionsDir)
|
|
70
|
-
.filter((f) => f.endsWith('.json'))
|
|
71
|
-
.map((f) => JSON.parse(fs.readFileSync(path.join(sessionsDir, f), 'utf8')));
|
|
72
|
-
|
|
73
|
-
const patterns = {
|
|
74
|
-
// Error patterns
|
|
75
|
-
commonErrors: extractCommonErrors(sessions),
|
|
76
|
-
|
|
77
|
-
// Tool usage patterns
|
|
78
|
-
toolUsage: extractToolPatterns(sessions),
|
|
79
|
-
|
|
80
|
-
// Feedback patterns
|
|
81
|
-
feedbackPatterns: extractFeedbackPatterns(sessions),
|
|
82
|
-
|
|
83
|
-
// Performance patterns
|
|
84
|
-
performanceByVariant: extractPerformanceByVariant(sessions),
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
return patterns;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function extractCommonErrors(sessions) {
|
|
91
|
-
const errorCounts = {};
|
|
92
|
-
|
|
93
|
-
sessions.forEach((s) => {
|
|
94
|
-
s.errors?.forEach((e) => {
|
|
95
|
-
const normalized = normalizeError(e.error);
|
|
96
|
-
errorCounts[normalized] = (errorCounts[normalized] || 0) + 1;
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
return Object.entries(errorCounts)
|
|
101
|
-
.sort((a, b) => b[1] - a[1])
|
|
102
|
-
.slice(0, 10)
|
|
103
|
-
.map(([error, count]) => ({ error, count }));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function normalizeError(error) {
|
|
107
|
-
// Normalize error messages for grouping
|
|
108
|
-
return error
|
|
109
|
-
.replace(/\d+/g, 'N')
|
|
110
|
-
.replace(/['"`][^'"`]+['"`]/g, '"..."')
|
|
111
|
-
.replace(/\/[^\s]+/g, '/path')
|
|
112
|
-
.slice(0, 100);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function extractToolPatterns(sessions) {
|
|
116
|
-
const toolStats = {};
|
|
117
|
-
|
|
118
|
-
sessions.forEach((s) => {
|
|
119
|
-
s.toolCalls?.forEach((t) => {
|
|
120
|
-
if (!toolStats[t.tool]) {
|
|
121
|
-
toolStats[t.tool] = { count: 0, success: 0, avgDuration: 0 };
|
|
122
|
-
}
|
|
123
|
-
toolStats[t.tool].count++;
|
|
124
|
-
if (t.success) toolStats[t.tool].success++;
|
|
125
|
-
toolStats[t.tool].avgDuration += t.duration || 0;
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Calculate averages
|
|
130
|
-
Object.values(toolStats).forEach((s) => {
|
|
131
|
-
s.avgDuration = s.count > 0 ? s.avgDuration / s.count : 0;
|
|
132
|
-
s.successRate = s.count > 0 ? s.success / s.count : 0;
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
return toolStats;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function extractFeedbackPatterns(sessions) {
|
|
139
|
-
const feedback = { positive: 0, negative: 0, corrections: 0, retries: 0 };
|
|
140
|
-
|
|
141
|
-
sessions.forEach((s) => {
|
|
142
|
-
s.userFeedback?.forEach((f) => {
|
|
143
|
-
if (f.type === 'thumbs_up') feedback.positive++;
|
|
144
|
-
if (f.type === 'thumbs_down') feedback.negative++;
|
|
145
|
-
if (f.type === 'correction') feedback.corrections++;
|
|
146
|
-
if (f.type === 'retry') feedback.retries++;
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
return feedback;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function extractPerformanceByVariant(sessions) {
|
|
154
|
-
const variants = {};
|
|
155
|
-
|
|
156
|
-
sessions.forEach((s) => {
|
|
157
|
-
if (!variants[s.variant]) {
|
|
158
|
-
variants[s.variant] = {
|
|
159
|
-
sessions: 0,
|
|
160
|
-
totalErrors: 0,
|
|
161
|
-
totalSuccess: 0,
|
|
162
|
-
avgDuration: 0,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
variants[s.variant].sessions++;
|
|
167
|
-
variants[s.variant].totalErrors += s.metrics?.errorCount || 0;
|
|
168
|
-
variants[s.variant].totalSuccess += s.metrics?.successfulToolCalls || 0;
|
|
169
|
-
variants[s.variant].avgDuration += s.duration || 0;
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// Normalize
|
|
173
|
-
Object.values(variants).forEach((v) => {
|
|
174
|
-
v.avgDuration = v.sessions > 0 ? v.avgDuration / v.sessions : 0;
|
|
175
|
-
v.errorRate = v.sessions > 0 ? v.totalErrors / v.sessions : 0;
|
|
176
|
-
v.successRate = v.totalSuccess / (v.totalSuccess + v.totalErrors) || 0;
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
return variants;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Generate reflection insights
|
|
184
|
-
*/
|
|
185
|
-
async function generateReflection() {
|
|
186
|
-
const patterns = analyzeSessionPatterns();
|
|
187
|
-
const state = JSON.parse(
|
|
188
|
-
fs.readFileSync(path.join(GEPA_DIR, 'state.json'), 'utf8')
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
// Load current best prompt for context
|
|
192
|
-
const currentPrompt = fs.readFileSync(
|
|
193
|
-
path.join(
|
|
194
|
-
GENERATIONS_DIR,
|
|
195
|
-
`gen-${String(state.currentGeneration).padStart(3, '0')}`,
|
|
196
|
-
`${state.bestVariant}.md`
|
|
197
|
-
),
|
|
198
|
-
'utf8'
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
const reflectionPrompt = `Analyze these AI agent performance patterns and generate specific improvement recommendations.
|
|
202
|
-
|
|
203
|
-
CURRENT SYSTEM PROMPT (excerpt):
|
|
204
|
-
\`\`\`markdown
|
|
205
|
-
${currentPrompt.slice(0, 3000)}...
|
|
206
|
-
\`\`\`
|
|
207
|
-
|
|
208
|
-
PERFORMANCE PATTERNS:
|
|
209
|
-
|
|
210
|
-
1. COMMON ERRORS (${patterns.commonErrors.length} types):
|
|
211
|
-
${patterns.commonErrors.map((e) => ` - "${e.error}" (${e.count}x)`).join('\n')}
|
|
212
|
-
|
|
213
|
-
2. TOOL USAGE:
|
|
214
|
-
${Object.entries(patterns.toolUsage)
|
|
215
|
-
.map(
|
|
216
|
-
([tool, s]) =>
|
|
217
|
-
` - ${tool}: ${s.count} calls, ${(s.successRate * 100).toFixed(0)}% success, ${s.avgDuration.toFixed(0)}ms avg`
|
|
218
|
-
)
|
|
219
|
-
.join('\n')}
|
|
220
|
-
|
|
221
|
-
3. USER FEEDBACK:
|
|
222
|
-
- Positive: ${patterns.feedbackPatterns.positive}
|
|
223
|
-
- Negative: ${patterns.feedbackPatterns.negative}
|
|
224
|
-
- Corrections needed: ${patterns.feedbackPatterns.corrections}
|
|
225
|
-
- Retries: ${patterns.feedbackPatterns.retries}
|
|
226
|
-
|
|
227
|
-
4. VARIANT PERFORMANCE:
|
|
228
|
-
${Object.entries(patterns.performanceByVariant)
|
|
229
|
-
.map(
|
|
230
|
-
([v, s]) =>
|
|
231
|
-
` - ${v}: ${s.sessions} sessions, ${s.errorRate.toFixed(1)} errors/session, ${(s.successRate * 100).toFixed(0)}% success`
|
|
232
|
-
)
|
|
233
|
-
.join('\n')}
|
|
234
|
-
|
|
235
|
-
Based on this data, provide:
|
|
236
|
-
|
|
237
|
-
1. TOP 3 FAILURE MODES - What's causing the most errors?
|
|
238
|
-
|
|
239
|
-
2. MISSING INSTRUCTIONS - What rules should be added to prevent errors?
|
|
240
|
-
|
|
241
|
-
3. UNCLEAR INSTRUCTIONS - What existing rules are being misinterpreted?
|
|
242
|
-
|
|
243
|
-
4. PRIORITY MUTATIONS - What specific changes would have highest impact?
|
|
244
|
-
|
|
245
|
-
Format as JSON:
|
|
246
|
-
{
|
|
247
|
-
"failureModes": ["...", "...", "..."],
|
|
248
|
-
"missingInstructions": ["...", "...", "..."],
|
|
249
|
-
"unclearInstructions": ["...", "...", "..."],
|
|
250
|
-
"priorityMutations": [
|
|
251
|
-
{"type": "add|modify|remove", "section": "...", "change": "...", "rationale": "..."},
|
|
252
|
-
...
|
|
253
|
-
]
|
|
254
|
-
}`;
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const result = await spawnClaude(reflectionPrompt);
|
|
258
|
-
|
|
259
|
-
// Parse JSON from response
|
|
260
|
-
const jsonMatch = result.match(/\{[\s\S]*\}/);
|
|
261
|
-
if (jsonMatch) {
|
|
262
|
-
const insights = JSON.parse(jsonMatch[0]);
|
|
263
|
-
|
|
264
|
-
// Save reflection
|
|
265
|
-
const reflectionPath = path.join(
|
|
266
|
-
RESULTS_DIR,
|
|
267
|
-
`reflection-${Date.now()}.json`
|
|
268
|
-
);
|
|
269
|
-
fs.writeFileSync(
|
|
270
|
-
reflectionPath,
|
|
271
|
-
JSON.stringify(
|
|
272
|
-
{
|
|
273
|
-
timestamp: new Date().toISOString(),
|
|
274
|
-
generation: state.currentGeneration,
|
|
275
|
-
patterns,
|
|
276
|
-
insights,
|
|
277
|
-
},
|
|
278
|
-
null,
|
|
279
|
-
2
|
|
280
|
-
)
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
return insights;
|
|
284
|
-
}
|
|
285
|
-
} catch (e) {
|
|
286
|
-
console.error('Reflection failed:', e.message);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Generate focused mutation prompts based on reflection
|
|
294
|
-
*/
|
|
295
|
-
function generateMutationGuide(insights) {
|
|
296
|
-
if (!insights) return null;
|
|
297
|
-
|
|
298
|
-
return `
|
|
299
|
-
REFLECTION-GUIDED MUTATIONS:
|
|
300
|
-
|
|
301
|
-
Based on ${insights.failureModes.length} identified failure modes:
|
|
302
|
-
${insights.failureModes.map((f, i) => `${i + 1}. ${f}`).join('\n')}
|
|
303
|
-
|
|
304
|
-
PRIORITY CHANGES:
|
|
305
|
-
${insights.priorityMutations
|
|
306
|
-
.map(
|
|
307
|
-
(m) =>
|
|
308
|
-
`- [${m.type.toUpperCase()}] ${m.section}: ${m.change}\n Rationale: ${m.rationale}`
|
|
309
|
-
)
|
|
310
|
-
.join('\n\n')}
|
|
311
|
-
|
|
312
|
-
MISSING INSTRUCTIONS TO ADD:
|
|
313
|
-
${insights.missingInstructions.map((i) => `- ${i}`).join('\n')}
|
|
314
|
-
|
|
315
|
-
UNCLEAR INSTRUCTIONS TO CLARIFY:
|
|
316
|
-
${insights.unclearInstructions.map((i) => `- ${i}`).join('\n')}
|
|
317
|
-
`;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// CLI
|
|
321
|
-
const command = process.argv[2];
|
|
322
|
-
|
|
323
|
-
switch (command) {
|
|
324
|
-
case 'analyze':
|
|
325
|
-
const patterns = analyzeSessionPatterns();
|
|
326
|
-
console.log(JSON.stringify(patterns, null, 2));
|
|
327
|
-
break;
|
|
328
|
-
|
|
329
|
-
case 'reflect':
|
|
330
|
-
generateReflection().then((insights) => {
|
|
331
|
-
if (insights) {
|
|
332
|
-
console.log('\nReflection Insights:');
|
|
333
|
-
console.log(JSON.stringify(insights, null, 2));
|
|
334
|
-
console.log('\nMutation Guide:');
|
|
335
|
-
console.log(generateMutationGuide(insights));
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
break;
|
|
339
|
-
|
|
340
|
-
default:
|
|
341
|
-
console.log(`
|
|
342
|
-
GEPA Reflection Engine
|
|
343
|
-
|
|
344
|
-
Usage:
|
|
345
|
-
node reflect.js analyze Analyze session patterns
|
|
346
|
-
node reflect.js reflect Generate reflection insights
|
|
347
|
-
`);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
export { analyzeSessionPatterns, generateReflection, generateMutationGuide };
|