@stackmemoryai/stackmemory 0.3.22 → 0.3.24

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.
Files changed (39) hide show
  1. package/dist/cli/commands/ralph.js +294 -0
  2. package/dist/cli/commands/ralph.js.map +7 -0
  3. package/dist/cli/index.js +2 -0
  4. package/dist/cli/index.js.map +2 -2
  5. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js +586 -0
  6. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +7 -0
  7. package/dist/integrations/ralph/context/context-budget-manager.js +297 -0
  8. package/dist/integrations/ralph/context/context-budget-manager.js.map +7 -0
  9. package/dist/integrations/ralph/context/stackmemory-context-loader.js +356 -0
  10. package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +7 -0
  11. package/dist/integrations/ralph/index.js +14 -0
  12. package/dist/integrations/ralph/index.js.map +7 -0
  13. package/dist/integrations/ralph/learning/pattern-learner.js +397 -0
  14. package/dist/integrations/ralph/learning/pattern-learner.js.map +7 -0
  15. package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js +444 -0
  16. package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js.map +7 -0
  17. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js +459 -0
  18. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +7 -0
  19. package/dist/integrations/ralph/performance/performance-optimizer.js +354 -0
  20. package/dist/integrations/ralph/performance/performance-optimizer.js.map +7 -0
  21. package/dist/integrations/ralph/ralph-integration-demo.js +178 -0
  22. package/dist/integrations/ralph/ralph-integration-demo.js.map +7 -0
  23. package/dist/integrations/ralph/state/state-reconciler.js +400 -0
  24. package/dist/integrations/ralph/state/state-reconciler.js.map +7 -0
  25. package/dist/integrations/ralph/swarm/swarm-coordinator.js +487 -0
  26. package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +7 -0
  27. package/dist/integrations/ralph/types.js +1 -0
  28. package/dist/integrations/ralph/types.js.map +7 -0
  29. package/dist/integrations/ralph/visualization/ralph-debugger.js +581 -0
  30. package/dist/integrations/ralph/visualization/ralph-debugger.js.map +7 -0
  31. package/package.json +1 -1
  32. package/scripts/deploy-ralph-swarm.sh +365 -0
  33. package/scripts/ralph-integration-test.js +274 -0
  34. package/scripts/ralph-loop-implementation.js +404 -0
  35. package/scripts/swarm-monitor.js +509 -0
  36. package/scripts/test-parallel-swarms.js +443 -0
  37. package/scripts/testing/ralph-cli-test.js +88 -0
  38. package/scripts/testing/ralph-integration-validation.js +727 -0
  39. package/scripts/testing/ralph-swarm-test-scenarios.js +613 -0
@@ -0,0 +1,509 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Real-time Swarm Monitoring Utility
5
+ * Provides live monitoring and metrics for running swarms
6
+ */
7
+
8
+ import * as fs from 'fs/promises';
9
+ import * as path from 'path';
10
+ import { createServer } from 'http';
11
+ import { WebSocketServer } from 'ws';
12
+ import blessed from 'blessed';
13
+
14
+ class SwarmMonitor {
15
+ constructor() {
16
+ this.swarmDir = '.swarm';
17
+ this.statusDir = path.join(this.swarmDir, 'status');
18
+ this.logsDir = path.join(this.swarmDir, 'logs');
19
+ this.metricsHistory = [];
20
+ this.maxHistorySize = 100;
21
+ this.updateInterval = 2000; // 2 seconds
22
+ this.wsPort = 3456;
23
+ }
24
+
25
+ async startMonitoring() {
26
+ console.log('🔍 Starting Swarm Monitor...');
27
+
28
+ // Check if we should use terminal UI or web interface
29
+ const mode = process.argv[2] || 'terminal';
30
+
31
+ if (mode === 'web') {
32
+ await this.startWebMonitor();
33
+ } else {
34
+ await this.startTerminalMonitor();
35
+ }
36
+ }
37
+
38
+ async startTerminalMonitor() {
39
+ // Create blessed screen
40
+ const screen = blessed.screen({
41
+ smartCSR: true,
42
+ title: 'Ralph Swarm Monitor'
43
+ });
44
+
45
+ // Create layout boxes
46
+ const header = blessed.box({
47
+ parent: screen,
48
+ top: 0,
49
+ left: 0,
50
+ width: '100%',
51
+ height: 3,
52
+ content: ' RALPH SWARM MONITOR ',
53
+ align: 'center',
54
+ style: {
55
+ fg: 'white',
56
+ bg: 'blue',
57
+ bold: true
58
+ }
59
+ });
60
+
61
+ const swarmList = blessed.list({
62
+ parent: screen,
63
+ top: 3,
64
+ left: 0,
65
+ width: '50%',
66
+ height: '40%',
67
+ label: ' Active Swarms ',
68
+ border: {
69
+ type: 'line'
70
+ },
71
+ style: {
72
+ fg: 'white',
73
+ border: {
74
+ fg: 'cyan'
75
+ },
76
+ selected: {
77
+ bg: 'blue'
78
+ }
79
+ },
80
+ mouse: true,
81
+ keys: true,
82
+ vi: true
83
+ });
84
+
85
+ const metricsBox = blessed.box({
86
+ parent: screen,
87
+ top: 3,
88
+ left: '50%',
89
+ width: '50%',
90
+ height: '40%',
91
+ label: ' Performance Metrics ',
92
+ border: {
93
+ type: 'line'
94
+ },
95
+ style: {
96
+ fg: 'white',
97
+ border: {
98
+ fg: 'yellow'
99
+ }
100
+ }
101
+ });
102
+
103
+ const logBox = blessed.log({
104
+ parent: screen,
105
+ bottom: 3,
106
+ left: 0,
107
+ width: '100%',
108
+ height: '55%',
109
+ label: ' Live Logs ',
110
+ border: {
111
+ type: 'line'
112
+ },
113
+ style: {
114
+ fg: 'white',
115
+ border: {
116
+ fg: 'green'
117
+ }
118
+ },
119
+ scrollable: true,
120
+ alwaysScroll: true,
121
+ mouse: true
122
+ });
123
+
124
+ const statusBar = blessed.box({
125
+ parent: screen,
126
+ bottom: 0,
127
+ left: 0,
128
+ width: '100%',
129
+ height: 3,
130
+ style: {
131
+ fg: 'white',
132
+ bg: 'black'
133
+ }
134
+ });
135
+
136
+ // Handle exit
137
+ screen.key(['q', 'C-c'], () => {
138
+ return process.exit(0);
139
+ });
140
+
141
+ // Update function
142
+ const updateDisplay = async () => {
143
+ try {
144
+ // Get swarm status
145
+ const swarms = await this.getActiveSwarms();
146
+
147
+ // Update swarm list
148
+ const swarmItems = swarms.map(s => {
149
+ const status = s.status === 'running' ? '🟢' : '🔴';
150
+ return `${status} ${s.id} - ${s.project}`;
151
+ });
152
+ swarmList.setItems(swarmItems);
153
+
154
+ // Update metrics
155
+ const metrics = await this.collectMetrics(swarms);
156
+ const metricsContent = this.formatMetrics(metrics);
157
+ metricsBox.setContent(metricsContent);
158
+
159
+ // Update status bar
160
+ const now = new Date().toLocaleTimeString();
161
+ statusBar.setContent(
162
+ ` Active: ${swarms.filter(s => s.status === 'running').length} | ` +
163
+ `Total: ${swarms.length} | ` +
164
+ `Updated: ${now} | ` +
165
+ `Press 'q' to quit`
166
+ );
167
+
168
+ screen.render();
169
+ } catch (error) {
170
+ logBox.log(`Error: ${error.message}`);
171
+ }
172
+ };
173
+
174
+ // Handle swarm selection
175
+ swarmList.on('select', async (item, index) => {
176
+ const swarms = await this.getActiveSwarms();
177
+ if (swarms[index]) {
178
+ await this.tailSwarmLogs(swarms[index], logBox);
179
+ }
180
+ });
181
+
182
+ // Start update loop
183
+ await updateDisplay();
184
+ setInterval(updateDisplay, this.updateInterval);
185
+
186
+ screen.render();
187
+ }
188
+
189
+ async startWebMonitor() {
190
+ // Create HTTP server for web interface
191
+ const server = createServer((req, res) => {
192
+ if (req.url === '/') {
193
+ res.writeHead(200, { 'Content-Type': 'text/html' });
194
+ res.end(this.getWebInterface());
195
+ } else if (req.url === '/api/swarms') {
196
+ this.handleApiRequest(res);
197
+ } else {
198
+ res.writeHead(404);
199
+ res.end('Not Found');
200
+ }
201
+ });
202
+
203
+ // Create WebSocket server for real-time updates
204
+ const wss = new WebSocketServer({ server });
205
+
206
+ wss.on('connection', (ws) => {
207
+ console.log('Client connected to monitor');
208
+
209
+ // Send initial data
210
+ this.sendSwarmUpdate(ws);
211
+
212
+ // Set up periodic updates
213
+ const updateInterval = setInterval(() => {
214
+ this.sendSwarmUpdate(ws);
215
+ }, this.updateInterval);
216
+
217
+ ws.on('close', () => {
218
+ clearInterval(updateInterval);
219
+ console.log('Client disconnected from monitor');
220
+ });
221
+ });
222
+
223
+ server.listen(this.wsPort, () => {
224
+ console.log(`📡 Web monitor running at http://localhost:${this.wsPort}`);
225
+ console.log('Open in browser to view real-time swarm status');
226
+ });
227
+ }
228
+
229
+ async getActiveSwarms() {
230
+ const swarms = [];
231
+
232
+ try {
233
+ const files = await fs.readdir(this.statusDir);
234
+
235
+ for (const file of files) {
236
+ if (file.endsWith('.json')) {
237
+ const filePath = path.join(this.statusDir, file);
238
+ const content = await fs.readFile(filePath, 'utf-8');
239
+ const swarm = JSON.parse(content);
240
+
241
+ // Check if process is still running
242
+ if (swarm.pid) {
243
+ try {
244
+ process.kill(swarm.pid, 0);
245
+ swarm.status = 'running';
246
+ } catch {
247
+ swarm.status = 'stopped';
248
+ }
249
+ }
250
+
251
+ swarms.push(swarm);
252
+ }
253
+ }
254
+ } catch (error) {
255
+ // Directory might not exist
256
+ }
257
+
258
+ return swarms;
259
+ }
260
+
261
+ async collectMetrics(swarms) {
262
+ const metrics = {
263
+ timestamp: Date.now(),
264
+ activeSwarms: swarms.filter(s => s.status === 'running').length,
265
+ totalSwarms: swarms.length,
266
+ agentCount: 0,
267
+ taskCompletion: 0,
268
+ memoryUsage: process.memoryUsage(),
269
+ cpuUsage: process.cpuUsage()
270
+ };
271
+
272
+ // Count total agents
273
+ for (const swarm of swarms) {
274
+ if (swarm.agents) {
275
+ metrics.agentCount += swarm.agents.split(',').length;
276
+ }
277
+ }
278
+
279
+ // Calculate average task completion (mock for now)
280
+ metrics.taskCompletion = Math.round(Math.random() * 100);
281
+
282
+ // Store in history
283
+ this.metricsHistory.push(metrics);
284
+ if (this.metricsHistory.length > this.maxHistorySize) {
285
+ this.metricsHistory.shift();
286
+ }
287
+
288
+ return metrics;
289
+ }
290
+
291
+ formatMetrics(metrics) {
292
+ const memoryMB = (metrics.memoryUsage.heapUsed / 1024 / 1024).toFixed(2);
293
+
294
+ return `
295
+ Active Swarms: ${metrics.activeSwarms}/${metrics.totalSwarms}
296
+ Total Agents: ${metrics.agentCount}
297
+ Task Completion: ${metrics.taskCompletion}%
298
+
299
+ Memory Usage: ${memoryMB} MB
300
+ CPU Time: ${metrics.cpuUsage.user / 1000}ms
301
+
302
+ Performance Trend:
303
+ ${this.getPerformanceTrend()}
304
+ `;
305
+ }
306
+
307
+ getPerformanceTrend() {
308
+ if (this.metricsHistory.length < 2) return 'Collecting data...';
309
+
310
+ const recent = this.metricsHistory.slice(-10);
311
+ let trend = '';
312
+
313
+ for (let i = 0; i < recent.length; i++) {
314
+ const value = recent[i].taskCompletion;
315
+ if (value > 80) trend += '█';
316
+ else if (value > 60) trend += '▓';
317
+ else if (value > 40) trend += '▒';
318
+ else if (value > 20) trend += '░';
319
+ else trend += ' ';
320
+ }
321
+
322
+ return trend;
323
+ }
324
+
325
+ async tailSwarmLogs(swarm, logBox) {
326
+ const logFile = swarm.logFile || path.join(this.logsDir, `${swarm.id}.log`);
327
+
328
+ try {
329
+ const content = await fs.readFile(logFile, 'utf-8');
330
+ const lines = content.split('\n');
331
+ const recentLines = lines.slice(-20);
332
+
333
+ logBox.log(`\n=== Logs for ${swarm.id} ===`);
334
+ recentLines.forEach(line => {
335
+ if (line.trim()) {
336
+ logBox.log(line);
337
+ }
338
+ });
339
+ } catch (error) {
340
+ logBox.log(`Could not read logs for ${swarm.id}: ${error.message}`);
341
+ }
342
+ }
343
+
344
+ async sendSwarmUpdate(ws) {
345
+ try {
346
+ const swarms = await this.getActiveSwarms();
347
+ const metrics = await this.collectMetrics(swarms);
348
+
349
+ ws.send(JSON.stringify({
350
+ type: 'update',
351
+ swarms,
352
+ metrics,
353
+ history: this.metricsHistory.slice(-20)
354
+ }));
355
+ } catch (error) {
356
+ console.error('Failed to send update:', error);
357
+ }
358
+ }
359
+
360
+ async handleApiRequest(res) {
361
+ try {
362
+ const swarms = await this.getActiveSwarms();
363
+ const metrics = await this.collectMetrics(swarms);
364
+
365
+ res.writeHead(200, { 'Content-Type': 'application/json' });
366
+ res.end(JSON.stringify({ swarms, metrics }));
367
+ } catch (error) {
368
+ res.writeHead(500);
369
+ res.end(JSON.stringify({ error: error.message }));
370
+ }
371
+ }
372
+
373
+ getWebInterface() {
374
+ return `
375
+ <!DOCTYPE html>
376
+ <html>
377
+ <head>
378
+ <title>Ralph Swarm Monitor</title>
379
+ <style>
380
+ body {
381
+ font-family: 'Monaco', 'Menlo', monospace;
382
+ background: #1e1e1e;
383
+ color: #d4d4d4;
384
+ margin: 0;
385
+ padding: 20px;
386
+ }
387
+ h1 {
388
+ color: #569cd6;
389
+ text-align: center;
390
+ }
391
+ .container {
392
+ display: grid;
393
+ grid-template-columns: 1fr 1fr;
394
+ gap: 20px;
395
+ margin-top: 20px;
396
+ }
397
+ .panel {
398
+ background: #2d2d30;
399
+ border: 1px solid #3e3e42;
400
+ border-radius: 5px;
401
+ padding: 15px;
402
+ }
403
+ .panel h2 {
404
+ color: #4ec9b0;
405
+ margin-top: 0;
406
+ }
407
+ .swarm-item {
408
+ padding: 8px;
409
+ margin: 5px 0;
410
+ background: #1e1e1e;
411
+ border-radius: 3px;
412
+ }
413
+ .status-running {
414
+ border-left: 3px solid #4ec9b0;
415
+ }
416
+ .status-stopped {
417
+ border-left: 3px solid #f44747;
418
+ }
419
+ .metrics {
420
+ font-size: 14px;
421
+ }
422
+ .metric-label {
423
+ color: #9cdcfe;
424
+ }
425
+ .metric-value {
426
+ color: #d4d4d4;
427
+ font-weight: bold;
428
+ }
429
+ #logs {
430
+ background: #1e1e1e;
431
+ padding: 10px;
432
+ height: 200px;
433
+ overflow-y: auto;
434
+ font-size: 12px;
435
+ white-space: pre-wrap;
436
+ }
437
+ </style>
438
+ </head>
439
+ <body>
440
+ <h1>🚀 Ralph Swarm Monitor</h1>
441
+
442
+ <div class="container">
443
+ <div class="panel">
444
+ <h2>Active Swarms</h2>
445
+ <div id="swarms"></div>
446
+ </div>
447
+
448
+ <div class="panel">
449
+ <h2>Performance Metrics</h2>
450
+ <div id="metrics" class="metrics"></div>
451
+ </div>
452
+ </div>
453
+
454
+ <div class="panel" style="margin-top: 20px;">
455
+ <h2>Live Logs</h2>
456
+ <div id="logs"></div>
457
+ </div>
458
+
459
+ <script>
460
+ const ws = new WebSocket('ws://localhost:${this.wsPort}');
461
+
462
+ ws.onmessage = (event) => {
463
+ const data = JSON.parse(event.data);
464
+ updateDisplay(data);
465
+ };
466
+
467
+ function updateDisplay(data) {
468
+ // Update swarms
469
+ const swarmsDiv = document.getElementById('swarms');
470
+ swarmsDiv.innerHTML = data.swarms.map(s => \`
471
+ <div class="swarm-item status-\${s.status}">
472
+ <strong>\${s.id}</strong><br>
473
+ Project: \${s.project}<br>
474
+ Agents: \${s.agents}<br>
475
+ Status: \${s.status}
476
+ </div>
477
+ \`).join('');
478
+
479
+ // Update metrics
480
+ const metricsDiv = document.getElementById('metrics');
481
+ metricsDiv.innerHTML = \`
482
+ <p><span class="metric-label">Active Swarms:</span> <span class="metric-value">\${data.metrics.activeSwarms}/\${data.metrics.totalSwarms}</span></p>
483
+ <p><span class="metric-label">Total Agents:</span> <span class="metric-value">\${data.metrics.agentCount}</span></p>
484
+ <p><span class="metric-label">Task Completion:</span> <span class="metric-value">\${data.metrics.taskCompletion}%</span></p>
485
+ <p><span class="metric-label">Memory Usage:</span> <span class="metric-value">\${(data.metrics.memoryUsage.heapUsed / 1024 / 1024).toFixed(2)} MB</span></p>
486
+ \`;
487
+
488
+ // Update logs (mock for now)
489
+ const logsDiv = document.getElementById('logs');
490
+ if (Math.random() > 0.7) {
491
+ const logEntry = new Date().toLocaleTimeString() + ' - Swarm activity detected\\n';
492
+ logsDiv.textContent += logEntry;
493
+ logsDiv.scrollTop = logsDiv.scrollHeight;
494
+ }
495
+ }
496
+ </script>
497
+ </body>
498
+ </html>
499
+ `;
500
+ }
501
+ }
502
+
503
+ // Run monitor if executed directly
504
+ if (import.meta.url === `file://${process.argv[1]}`) {
505
+ const monitor = new SwarmMonitor();
506
+ monitor.startMonitoring().catch(console.error);
507
+ }
508
+
509
+ export { SwarmMonitor };