@yemi33/minions 0.1.764 → 0.1.765

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,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.765 (2026-04-10)
4
+
5
+ ### Features
6
+ - id/kill endpoint
7
+
3
8
  ## 0.1.764 (2026-04-10)
4
9
 
5
10
  ### Features
package/dashboard.js CHANGED
@@ -1459,6 +1459,70 @@ 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
+ const safePid = shared.validatePid(pid);
1476
+ if (process.platform === 'win32') {
1477
+ require('child_process').execFileSync('taskkill', ['/PID', String(safePid), '/F', '/T'], { stdio: 'pipe', timeout: 5000, windowsHide: true });
1478
+ } else {
1479
+ process.kill(safePid, 'SIGTERM');
1480
+ }
1481
+ }
1482
+ } catch { /* process already dead or no pid file */ }
1483
+ try { fs.unlinkSync(pidPath); } catch { /* optional */ }
1484
+
1485
+ // 2. Clear session.json so retry starts fresh
1486
+ try { fs.unlinkSync(path.join(agentDir, 'session.json')); } catch { /* optional */ }
1487
+
1488
+ // 3. Remove all active dispatch entries for this agent
1489
+ const dispatchPath = path.join(MINIONS_DIR, 'engine', 'dispatch.json');
1490
+ const removedIds = [];
1491
+ mutateJsonFileLocked(dispatchPath, (dp) => {
1492
+ const before = (dp.active || []).length;
1493
+ const removed = (dp.active || []).filter(d => d.agent === agentId);
1494
+ removed.forEach(d => removedIds.push(d.id));
1495
+ dp.active = (dp.active || []).filter(d => d.agent !== agentId);
1496
+ return dp;
1497
+ }, { defaultValue: { pending: [], active: [], completed: [] } });
1498
+
1499
+ // 4. Reset work items from dispatched → pending so they can be retried
1500
+ const allWiPaths = [];
1501
+ for (const proj of PROJECTS) allWiPaths.push(shared.projectWorkItemsPath(proj));
1502
+ let resetCount = 0;
1503
+ for (const wiPath of allWiPaths) {
1504
+ try {
1505
+ mutateWorkItems(wiPath, items => {
1506
+ for (const item of items) {
1507
+ if (item.dispatched_to === agentId && item.status === WI_STATUS.DISPATCHED) {
1508
+ item.status = WI_STATUS.PENDING;
1509
+ item._retryCount = (item._retryCount || 0) + 1;
1510
+ delete item.dispatched_at;
1511
+ delete item.dispatched_to;
1512
+ delete item._pendingReason;
1513
+ resetCount++;
1514
+ }
1515
+ }
1516
+ return items;
1517
+ });
1518
+ } catch { /* optional */ }
1519
+ }
1520
+
1521
+ invalidateStatusCache();
1522
+ return jsonReply(res, 200, { ok: true, agent: agentId, dispatchCleared: removedIds.length, workItemsReset: resetCount });
1523
+ } catch (e) { return jsonReply(res, 400, { error: e.message }); }
1524
+ }
1525
+
1462
1526
  async function handleAgentsCancel(req, res) {
1463
1527
  try {
1464
1528
  const body = await readBody(req);
@@ -4027,6 +4091,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
4027
4091
  return jsonReply(res, 200, { ok: true, message: 'Steering message sent' });
4028
4092
  }},
4029
4093
  { method: 'POST', path: '/api/agents/cancel', desc: 'Cancel an active agent by ID or task substring', params: 'agent?, task?', handler: handleAgentsCancel },
4094
+ { method: 'POST', path: /^\/api\/agent\/([\w-]+)\/kill$/, desc: 'Kill a running agent: stop process, clear dispatch, reset work items to pending', handler: handleAgentKill },
4030
4095
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/live-stream(?:\?.*)?$/, desc: 'SSE real-time live output streaming', handler: handleAgentLiveStream },
4031
4096
  { method: 'GET', path: /^\/api\/agent\/([\w-]+)\/live(?:\?.*)?$/, desc: 'Tail live output for a working agent', params: 'tail? (bytes, default 8192)', handler: handleAgentLive },
4032
4097
  { 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.765",
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"