@yemi33/minions 0.1.1972 → 0.1.1973
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/engine/cli.js +8 -1
- package/engine/spawn-agent.js +36 -16
- package/package.json +1 -1
package/engine/cli.js
CHANGED
|
@@ -521,7 +521,14 @@ const commands = {
|
|
|
521
521
|
const savedBranch = normalizeSessionBranch(sj?.branch);
|
|
522
522
|
if (sj?.sessionId && (!expectedBranch || savedBranch === expectedBranch)) {
|
|
523
523
|
sessionId = sj.sessionId;
|
|
524
|
-
} else if (sj?.sessionId && expectedBranch) {
|
|
524
|
+
} else if (sj?.sessionId && expectedBranch && sj?.dispatchId === item.id) {
|
|
525
|
+
// Only warn when the saved session is for THIS dispatch but on the
|
|
526
|
+
// wrong branch — that's a true anomaly worth flagging. The common
|
|
527
|
+
// case — leftover session.json from a previous (now-completed)
|
|
528
|
+
// dispatch on a different branch — is expected and silent, since
|
|
529
|
+
// the engine writes session.json on completion of each dispatch
|
|
530
|
+
// and a fresh dispatch may run on a different branch before
|
|
531
|
+
// saveSession overwrites it (W-mpbn93ou000611b3).
|
|
525
532
|
shared.log('warn', `Reattach: ignoring session for ${agentId} on branch ${savedBranch || 'unknown'}; expected ${expectedBranch}`);
|
|
526
533
|
}
|
|
527
534
|
} catch {}
|
package/engine/spawn-agent.js
CHANGED
|
@@ -529,9 +529,44 @@ function main() {
|
|
|
529
529
|
clearTimeout(startupTimer);
|
|
530
530
|
clearTimeout(initialSnapshotTimer);
|
|
531
531
|
clearInterval(descTimer);
|
|
532
|
+
|
|
533
|
+
// Compute the exit code and write the [process-exit] sentinel FIRST,
|
|
534
|
+
// before the descendant snapshot/reap. The engine's orphan reaper uses
|
|
535
|
+
// the sentinel as the single signal that "the runtime exited cleanly
|
|
536
|
+
// with code N"; if we delay it behind `snapshotDescendants()` (which
|
|
537
|
+
// shells out to `Get-CimInstance` on Windows and can block 1-5+s),
|
|
538
|
+
// there's a window where the runtime PID we track is already dead but
|
|
539
|
+
// no sentinel exists yet. After an engine restart that path triggers
|
|
540
|
+
// `canReapDeadProcess` and the dispatch gets auto-retried as orphaned
|
|
541
|
+
// even though it completed normally. See W-mpbn93ou000611b3 / the
|
|
542
|
+
// 2026-05-18 ripley-explore regression.
|
|
543
|
+
//
|
|
544
|
+
// Prefer the 'exit' event's code/signal when present (Node's 'close'
|
|
545
|
+
// event can report code=0 on Windows when the OS-level exit was
|
|
546
|
+
// non-zero — see the long-form note above the exit handler).
|
|
547
|
+
const effectiveCode = (realExitFromEvent != null) ? realExitFromEvent : code;
|
|
548
|
+
const effectiveSignal = realSignalFromEvent || signal;
|
|
549
|
+
const exitCode = normalizeRuntimeExit(effectiveCode, effectiveSignal);
|
|
550
|
+
if (sentinelWritten) {
|
|
551
|
+
// Defense-in-depth: never write a duplicate sentinel. We observed pairs
|
|
552
|
+
// of [process-exit] code=0 lines in live-output.log across many failed
|
|
553
|
+
// runs, which suggests close has fired twice in some edge cases (e.g.,
|
|
554
|
+
// shim re-launch on Windows). One sentinel per spawn is the contract.
|
|
555
|
+
// Skip descendant reap on the duplicate close too — the first close
|
|
556
|
+
// already handled it (reaping the same PIDs again is a no-op at best,
|
|
557
|
+
// but skipping is faster and matches the prior early-return contract).
|
|
558
|
+
fs.appendFileSync(debugPath, `EXIT (duplicate close, skipping sentinel): code=${exitCode}${effectiveSignal ? ` signal=${effectiveSignal}` : ''}\n`);
|
|
559
|
+
process.exit(exitCode);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
sentinelWritten = true;
|
|
563
|
+
const sentinelResult = writeProcessExitSentinel({ exitCode, signal: effectiveSignal });
|
|
564
|
+
|
|
532
565
|
// Final snapshot + reap, but only when the runtime actually spawned
|
|
533
566
|
// children. Read-only / very short agents (exit before the 3s initial
|
|
534
|
-
// snapshot fires) skip the wmic shell-out entirely.
|
|
567
|
+
// snapshot fires) skip the wmic shell-out entirely. Runs AFTER the
|
|
568
|
+
// sentinel write so a slow Get-CimInstance call can't gate completion
|
|
569
|
+
// detection — see the hoist note above.
|
|
535
570
|
if (trackedDescendants.size || gotFirstOutput) {
|
|
536
571
|
snapshotDescendants();
|
|
537
572
|
if (trackedDescendants.size) {
|
|
@@ -580,21 +615,6 @@ function main() {
|
|
|
580
615
|
try { fs.appendFileSync(debugPath, `DESCENDANTS reaped=${reaped}/${toKillPids.length} kept=${kept.length}\n`); } catch {}
|
|
581
616
|
}
|
|
582
617
|
}
|
|
583
|
-
// Prefer the 'exit' event's code/signal when present — see note above.
|
|
584
|
-
const effectiveCode = (realExitFromEvent != null) ? realExitFromEvent : code;
|
|
585
|
-
const effectiveSignal = realSignalFromEvent || signal;
|
|
586
|
-
const exitCode = normalizeRuntimeExit(effectiveCode, effectiveSignal);
|
|
587
|
-
if (sentinelWritten) {
|
|
588
|
-
// Defense-in-depth: never write a duplicate sentinel. We observed pairs
|
|
589
|
-
// of [process-exit] code=0 lines in live-output.log across many failed
|
|
590
|
-
// runs, which suggests close has fired twice in some edge cases (e.g.,
|
|
591
|
-
// shim re-launch on Windows). One sentinel per spawn is the contract.
|
|
592
|
-
fs.appendFileSync(debugPath, `EXIT (duplicate close, skipping sentinel): code=${exitCode}${effectiveSignal ? ` signal=${effectiveSignal}` : ''}\n`);
|
|
593
|
-
process.exit(exitCode);
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
sentinelWritten = true;
|
|
597
|
-
const sentinelResult = writeProcessExitSentinel({ exitCode, signal: effectiveSignal });
|
|
598
618
|
fs.appendFileSync(debugPath, `EXIT: code=${exitCode}${effectiveSignal ? ` signal=${effectiveSignal}` : ''} (close=${code} exit=${realExitFromEvent})\nSTDERR: ${stderrBuf.slice(0, 500)}\n`);
|
|
599
619
|
if (!sentinelResult.fileWritten) {
|
|
600
620
|
fs.appendFileSync(debugPath, `EXIT SENTINEL: file write failed for ${process.env.MINIONS_LIVE_OUTPUT_PATH}\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1973",
|
|
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"
|