@yemi33/minions 0.1.764 → 0.1.766

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.766 (2026-04-10)
4
+
5
+ ### Features
6
+ - id/kill endpoint
7
+
8
+ ### Fixes
9
+ - harden agent kill endpoint — use killGracefully, clean steer.md, remove dead var
10
+
3
11
  ## 0.1.764 (2026-04-10)
4
12
 
5
13
  ### Features
package/dashboard.js CHANGED
@@ -1459,6 +1459,66 @@ const server = http.createServer(async (req, res) => {
1459
1459
  } catch (e) { return jsonReply(res, 400, { error: e.message }); }
1460
1460
  }
1461
1461
 
1462
+ async function handleAgentKill(req, res, match) {
1463
+ try {
1464
+ const agentId = match[1].replace(/[^a-zA-Z0-9_-]/g, '');
1465
+ if (!agentId) return jsonReply(res, 400, { error: 'agent id required' });
1466
+
1467
+ const agentDir = path.join(MINIONS_DIR, 'agents', agentId);
1468
+ if (!fs.existsSync(agentDir)) return jsonReply(res, 404, { error: 'Agent not found' });
1469
+
1470
+ // 1. Kill process via pid file
1471
+ const pidPath = path.join(agentDir, 'pid');
1472
+ try {
1473
+ const pid = parseInt(shared.safeRead(pidPath) || '', 10);
1474
+ if (pid) {
1475
+ shared.validatePid(pid); // throws if not numeric
1476
+ shared.killGracefully({ pid }, 3000);
1477
+ }
1478
+ } catch { /* process already dead or no pid file */ }
1479
+ try { fs.unlinkSync(pidPath); } catch { /* optional */ }
1480
+
1481
+ // 2. Clear session.json and steer.md so retry starts fresh
1482
+ try { fs.unlinkSync(path.join(agentDir, 'session.json')); } catch { /* optional */ }
1483
+ try { fs.unlinkSync(path.join(agentDir, 'steer.md')); } catch { /* optional */ }
1484
+
1485
+ // 3. Remove all active dispatch entries for this agent
1486
+ const dispatchPath = path.join(MINIONS_DIR, 'engine', 'dispatch.json');
1487
+ const removedIds = [];
1488
+ mutateJsonFileLocked(dispatchPath, (dp) => {
1489
+ const removed = (dp.active || []).filter(d => d.agent === agentId);
1490
+ removed.forEach(d => removedIds.push(d.id));
1491
+ dp.active = (dp.active || []).filter(d => d.agent !== agentId);
1492
+ return dp;
1493
+ }, { defaultValue: { pending: [], active: [], completed: [] } });
1494
+
1495
+ // 4. Reset work items from dispatched → pending so they can be retried
1496
+ const allWiPaths = [];
1497
+ for (const proj of PROJECTS) allWiPaths.push(shared.projectWorkItemsPath(proj));
1498
+ let resetCount = 0;
1499
+ for (const wiPath of allWiPaths) {
1500
+ try {
1501
+ mutateWorkItems(wiPath, items => {
1502
+ for (const item of items) {
1503
+ if (item.dispatched_to === agentId && item.status === WI_STATUS.DISPATCHED) {
1504
+ item.status = WI_STATUS.PENDING;
1505
+ item._retryCount = (item._retryCount || 0) + 1;
1506
+ delete item.dispatched_at;
1507
+ delete item.dispatched_to;
1508
+ delete item._pendingReason;
1509
+ resetCount++;
1510
+ }
1511
+ }
1512
+ return items;
1513
+ });
1514
+ } catch { /* optional */ }
1515
+ }
1516
+
1517
+ invalidateStatusCache();
1518
+ return jsonReply(res, 200, { ok: true, agent: agentId, dispatchCleared: removedIds.length, workItemsReset: resetCount });
1519
+ } catch (e) { return jsonReply(res, 400, { error: e.message }); }
1520
+ }
1521
+
1462
1522
  async function handleAgentsCancel(req, res) {
1463
1523
  try {
1464
1524
  const body = await readBody(req);
@@ -4027,6 +4087,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
4027
4087
  return jsonReply(res, 200, { ok: true, message: 'Steering message sent' });
4028
4088
  }},
4029
4089
  { method: 'POST', path: '/api/agents/cancel', desc: 'Cancel an active agent by ID or task substring', params: 'agent?, task?', handler: handleAgentsCancel },
4090
+ { method: 'POST', path: /^\/api\/agent\/([\w-]+)\/kill$/, desc: 'Kill a running agent: stop process, clear dispatch, reset work items to pending', handler: handleAgentKill },
4030
4091
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/live-stream(?:\?.*)?$/, desc: 'SSE real-time live output streaming', handler: handleAgentLiveStream },
4031
4092
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/live(?:\?.*)?$/, desc: 'Tail live output for a working agent', params: 'tail? (bytes, default 8192)', handler: handleAgentLive },
4032
4093
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/output(?:\?.*)?$/, desc: 'Fetch final output.log for an agent', handler: handleAgentOutput },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.764",
3
+ "version": "0.1.766",
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"