@yemi33/minions 0.1.1048 → 0.1.1049

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,6 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1048 (2026-04-17)
3
+ ## 0.1.1049 (2026-04-17)
4
+
5
+ ### Features
6
+ - seed realActivityMap at spawn time, stamp pid in live-output (#1200)
4
7
 
5
8
  ### Fixes
6
9
  - stamp live-output.log stub before spawn (#1198)
package/engine/timeout.js CHANGED
@@ -308,15 +308,33 @@ function checkTimeouts(config) {
308
308
  const procInfo = activeProcesses.get(item.id);
309
309
  if (procInfo?._steeringAt && Date.now() - procInfo._steeringAt < 60000) continue;
310
310
 
311
- // Capture live-output.log file state for orphan/hung diagnostics (#W-mo248lkjwgsu).
312
- // Three distinguishable failure modes:
313
- // logExists=false → spawn call itself threw, no log ever written
314
- // logExists=true, size~stub process died at startup (stub-only content)
315
- // logExists=true, size>stub genuine hang (process alive, wrote output, then stopped)
316
- let _logState = 'logExists=false logSize=0';
311
+ // Capture live-output.log file state for orphan/hung diagnostics
312
+ // (#W-mo248lkjwgsu original, #W-mo25loq8kjer pid annotation).
313
+ // Four distinguishable failure modes:
314
+ // logExists=false → spawn call itself threw, no log ever written
315
+ // logExists=true pidPresent=false → engine stub written but spawn died before emitting pid line
316
+ // logExists=true pidPresent=true silent → process spawned (pid recorded) but never produced stdout
317
+ // logExists=true pidPresent=true size>pid → genuine hang (process wrote output then stopped)
318
+ //
319
+ // The pid line `[<iso>] pid: <N>` is stamped by engine.js immediately after runFile() returns.
320
+ // Its presence → the child process was actually spawned; absence → spawn itself failed or the
321
+ // appendFileSync on the pid line threw (rare).
322
+ let _logState = 'logExists=false logSize=0 pidPresent=false';
317
323
  try {
318
324
  const lst = fs.statSync(liveLogPath);
319
- _logState = `logExists=true logSize=${lst.size}`;
325
+ // Read only the head (4KB) — pid line is written right after the stub, always near the top.
326
+ // Avoids loading the full log just for a diagnostic annotation.
327
+ let pidPresent = false;
328
+ try {
329
+ const fd = fs.openSync(liveLogPath, 'r');
330
+ const headSize = Math.min(lst.size, 4096);
331
+ const headBuf = Buffer.alloc(headSize);
332
+ fs.readSync(fd, headBuf, 0, headSize, 0);
333
+ fs.closeSync(fd);
334
+ // Match `] pid: <digits>` — agnostic to ISO timestamp format at the start.
335
+ pidPresent = /\]\s+pid:\s+\d+/.test(headBuf.toString('utf8'));
336
+ } catch { /* read failure — pidPresent stays false */ }
337
+ _logState = `logExists=true logSize=${lst.size} pidPresent=${pidPresent}`;
320
338
  } catch { /* ENOENT — keep default */ }
321
339
 
322
340
  if (!hasProcess && silentMs > effectiveTimeout && (Date.now() > engineRestartGraceUntil || engineRestartGraceExempt?.has(item.id))) {
package/engine.js CHANGED
@@ -916,6 +916,23 @@ async function spawnAgent(dispatchItem, config) {
916
916
  throw spawnErr;
917
917
  }
918
918
 
919
+ // Seed realActivityMap and stamp PID immediately — BEFORE any handlers or timers (#W-mo25loq8kjer).
920
+ // Why NOW, not later in the function:
921
+ // 1. Heartbeat clock anchoring. timeout.js uses realActivityMap as the last-activity timestamp for
922
+ // tracked processes; when the map has no entry, it falls back to item.started_at (dispatch time,
923
+ // which is 20-60s before actual spawn for write tasks doing worktree setup). Read-only tasks
924
+ // that produce no stdout for minutes (explore, security audit, large scans) were hitting
925
+ // heartbeatTimeout prematurely — clock had already been running since dispatch.
926
+ // 2. Error-handler race. The `proc.on('error', ...)` handler below calls realActivityMap.delete(id)
927
+ // on synchronous spawn failures. Seeding before registering handlers ensures delete sees a value
928
+ // to clear rather than leaving an absent-then-absent no-op that downstream code must guard.
929
+ // 3. Orphan diagnostics. The PID line gives timeout.js a deterministic way to tell "spawn died
930
+ // before first write" (stub-only log) from "process started and is hung" (stub + pid line).
931
+ realActivityMap.set(id, Date.now());
932
+ try {
933
+ fs.appendFileSync(liveOutputPath, `[${new Date().toISOString()}] pid: ${proc.pid ?? 'unknown'}\n`);
934
+ } catch { /* log stamp is best-effort — don't block spawn on fs failure */ }
935
+
919
936
  const MAX_OUTPUT = 1024 * 1024; // 1MB
920
937
  let stdout = '';
921
938
  let stderr = '';
@@ -1292,9 +1309,11 @@ async function spawnAgent(dispatchItem, config) {
1292
1309
  }
1293
1310
  }, 3000);
1294
1311
 
1295
- // Track process — even if PID isn't available yet (async on Windows)
1312
+ // Track process — even if PID isn't available yet (async on Windows).
1313
+ // realActivityMap was already seeded immediately after runFile() returned (#W-mo25loq8kjer);
1314
+ // don't re-seed here — the stdout/stderr handlers above can already have updated it with
1315
+ // a fresher timestamp, and overwriting would clobber the real "last activity" signal.
1296
1316
  activeProcesses.set(id, { proc, agentId, startedAt, sessionId: cachedSessionId });
1297
- realActivityMap.set(id, Date.now()); // Initialize real activity at spawn time
1298
1317
 
1299
1318
  updateAgentStatus(id, AGENT_STATUS.RUNNING, `Process spawned for ${agentId}`);
1300
1319
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1048",
3
+ "version": "0.1.1049",
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"