@yemi33/minions 0.1.600 → 0.1.601
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/CHANGELOG.md +2 -1
- package/dashboard.js +60 -1
- package/engine/cleanup.js +12 -1
- package/engine.js +14 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.601 (2026-04-08)
|
|
4
4
|
|
|
5
5
|
### Fixes
|
|
6
|
+
- rotate live-output.log on spawn to preserve previous session (closes #543) (#551)
|
|
6
7
|
- restore pendingFix on failed human-feedback dispatch (closes #540) (#550)
|
|
7
8
|
|
|
8
9
|
## 0.1.599 (2026-04-08)
|
package/dashboard.js
CHANGED
|
@@ -1499,6 +1499,26 @@ const server = http.createServer(async (req, res) => {
|
|
|
1499
1499
|
try {
|
|
1500
1500
|
const stat = fs.statSync(liveLogPath);
|
|
1501
1501
|
const fileSize = stat.size;
|
|
1502
|
+
|
|
1503
|
+
// Fall back to previous session log when current is sparse (fixes #543)
|
|
1504
|
+
const SPARSE_THRESHOLD = 500;
|
|
1505
|
+
if (fileSize < SPARSE_THRESHOLD) {
|
|
1506
|
+
const prevPath = path.join(agentDir, 'live-output-prev.log');
|
|
1507
|
+
try {
|
|
1508
|
+
const prevStat = fs.statSync(prevPath);
|
|
1509
|
+
if (prevStat.size > SPARSE_THRESHOLD) {
|
|
1510
|
+
const prevTailBytes = Math.min(tailBytes, prevStat.size);
|
|
1511
|
+
const prevStart = Math.max(0, prevStat.size - prevTailBytes);
|
|
1512
|
+
const prevFd = fs.openSync(prevPath, 'r');
|
|
1513
|
+
const prevBuf = Buffer.alloc(prevStat.size - prevStart);
|
|
1514
|
+
fs.readSync(prevFd, prevBuf, 0, prevBuf.length, prevStart);
|
|
1515
|
+
fs.closeSync(prevFd);
|
|
1516
|
+
const prevContent = prevBuf.toString('utf8');
|
|
1517
|
+
if (prevContent) safeWrite(`data: ${JSON.stringify(prevContent + '\n\n--- previous session (new session starting) ---\n\n')}\n\n`);
|
|
1518
|
+
}
|
|
1519
|
+
} catch { /* prev file may not exist — that's fine */ }
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1502
1522
|
if (fileSize > 0) {
|
|
1503
1523
|
const readStart = Math.max(0, fileSize - tailBytes);
|
|
1504
1524
|
const readLen = fileSize - readStart;
|
|
@@ -1565,7 +1585,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
1565
1585
|
|
|
1566
1586
|
async function handleAgentLive(req, res, match) {
|
|
1567
1587
|
const agentId = match[1];
|
|
1568
|
-
const
|
|
1588
|
+
const agentDir = path.join(MINIONS_DIR, 'agents', agentId);
|
|
1589
|
+
const livePath = path.join(agentDir, 'live-output.log');
|
|
1590
|
+
const prevPath = path.join(agentDir, 'live-output-prev.log');
|
|
1569
1591
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
1570
1592
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
1571
1593
|
const params = new URL(req.url, 'http://localhost').searchParams;
|
|
@@ -1576,6 +1598,28 @@ const server = http.createServer(async (req, res) => {
|
|
|
1576
1598
|
try {
|
|
1577
1599
|
const stat = fs.statSync(livePath);
|
|
1578
1600
|
if (stat.size === 0) { res.end('No live output. Agent may not be running.'); return; }
|
|
1601
|
+
|
|
1602
|
+
// Fall back to previous session log when current is sparse (fixes #543)
|
|
1603
|
+
// Sparse = only header + init JSON, typically < 500 bytes
|
|
1604
|
+
const SPARSE_THRESHOLD = 500;
|
|
1605
|
+
if (stat.size < SPARSE_THRESHOLD && fs.existsSync(prevPath)) {
|
|
1606
|
+
try {
|
|
1607
|
+
const prevStat = fs.statSync(prevPath);
|
|
1608
|
+
if (prevStat.size > SPARSE_THRESHOLD) {
|
|
1609
|
+
// Prepend separator + previous session tail, then append current sparse content
|
|
1610
|
+
const prevTailBytes = Math.max(1, Math.min(tailBytes - stat.size - 100, prevStat.size));
|
|
1611
|
+
const prevStart = Math.max(0, prevStat.size - prevTailBytes);
|
|
1612
|
+
const prevBuf = Buffer.alloc(Math.min(prevTailBytes, prevStat.size));
|
|
1613
|
+
const prevFd = fs.openSync(prevPath, 'r');
|
|
1614
|
+
fs.readSync(prevFd, prevBuf, 0, prevBuf.length, prevStart);
|
|
1615
|
+
fs.closeSync(prevFd);
|
|
1616
|
+
const currentBuf = fs.readFileSync(livePath, 'utf8');
|
|
1617
|
+
res.end(prevBuf.toString('utf8') + '\n\n--- previous session (new session starting) ---\n\n' + currentBuf);
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
} catch { /* fall through to normal read */ }
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1579
1623
|
const start = Math.max(0, stat.size - tailBytes);
|
|
1580
1624
|
const buf = Buffer.alloc(Math.min(tailBytes, stat.size));
|
|
1581
1625
|
const fd = fs.openSync(livePath, 'r');
|
|
@@ -1583,6 +1627,21 @@ const server = http.createServer(async (req, res) => {
|
|
|
1583
1627
|
fs.closeSync(fd);
|
|
1584
1628
|
res.end(buf.toString('utf8'));
|
|
1585
1629
|
} catch {
|
|
1630
|
+
// If live-output.log doesn't exist but prev does, serve that
|
|
1631
|
+
try {
|
|
1632
|
+
if (fs.existsSync(prevPath)) {
|
|
1633
|
+
const prevStat = fs.statSync(prevPath);
|
|
1634
|
+
if (prevStat.size > 0) {
|
|
1635
|
+
const start = Math.max(0, prevStat.size - tailBytes);
|
|
1636
|
+
const buf = Buffer.alloc(Math.min(tailBytes, prevStat.size));
|
|
1637
|
+
const fd = fs.openSync(prevPath, 'r');
|
|
1638
|
+
fs.readSync(fd, buf, 0, buf.length, start);
|
|
1639
|
+
fs.closeSync(fd);
|
|
1640
|
+
res.end(buf.toString('utf8') + '\n\n--- previous session (current session output unavailable) ---\n');
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
} catch { /* fall through */ }
|
|
1586
1645
|
res.end('No live output. Agent may not be running.');
|
|
1587
1646
|
}
|
|
1588
1647
|
return;
|
package/engine/cleanup.js
CHANGED
|
@@ -76,7 +76,7 @@ function runCleanup(config, verbose = false) {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// 2. Clean live-output.log for idle agents (not currently working)
|
|
79
|
+
// 2. Clean live-output.log and live-output-prev.log for idle agents (not currently working)
|
|
80
80
|
for (const [agentId] of Object.entries(config.agents || {})) {
|
|
81
81
|
const status = getAgentStatus(agentId);
|
|
82
82
|
if (status.status !== 'working') {
|
|
@@ -90,6 +90,17 @@ function runCleanup(config, verbose = false) {
|
|
|
90
90
|
}
|
|
91
91
|
} catch { /* cleanup */ }
|
|
92
92
|
}
|
|
93
|
+
// Also clean rotated previous session log
|
|
94
|
+
const prevPath = path.join(AGENTS_DIR, agentId, 'live-output-prev.log');
|
|
95
|
+
if (fs.existsSync(prevPath)) {
|
|
96
|
+
try {
|
|
97
|
+
const stat = fs.statSync(prevPath);
|
|
98
|
+
if (stat.mtimeMs < oneHourAgo) {
|
|
99
|
+
fs.unlinkSync(prevPath);
|
|
100
|
+
cleaned.liveOutputs++;
|
|
101
|
+
}
|
|
102
|
+
} catch { /* cleanup */ }
|
|
103
|
+
}
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
|
package/engine.js
CHANGED
|
@@ -560,6 +560,20 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
560
560
|
|
|
561
561
|
// Live output file — written as data arrives so dashboard can tail it
|
|
562
562
|
const liveOutputPath = path.join(AGENTS_DIR, agentId, 'live-output.log');
|
|
563
|
+
|
|
564
|
+
// Rotate previous live output to preserve session history (fixes #543: orphan recovery overwrites)
|
|
565
|
+
// Only rotate if the existing file has meaningful content (beyond just the header)
|
|
566
|
+
const LIVE_OUTPUT_SPARSE_THRESHOLD = 500; // bytes — header + init JSON is typically < 500
|
|
567
|
+
try {
|
|
568
|
+
if (fs.existsSync(liveOutputPath)) {
|
|
569
|
+
const prevStat = fs.statSync(liveOutputPath);
|
|
570
|
+
if (prevStat.size > LIVE_OUTPUT_SPARSE_THRESHOLD) {
|
|
571
|
+
const prevPath = path.join(AGENTS_DIR, agentId, 'live-output-prev.log');
|
|
572
|
+
fs.renameSync(liveOutputPath, prevPath);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
} catch { /* rotation is best-effort — overwrite still happens below */ }
|
|
576
|
+
|
|
563
577
|
safeWrite(liveOutputPath, `# Live output for ${agentId} — ${id}\n# Started: ${startedAt}\n# Task: ${dispatchItem.task}\n\n`);
|
|
564
578
|
|
|
565
579
|
// Keep live log active even when the agent produces no stdout/stderr for long stretches.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.601",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|