@yemi33/minions 0.1.738 → 0.1.739
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 +5 -0
- package/engine/timeout.js +14 -5
- package/engine.js +9 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/engine/timeout.js
CHANGED
|
@@ -159,11 +159,20 @@ function checkTimeouts(config) {
|
|
|
159
159
|
const liveLogPath = path.join(AGENTS_DIR, item.agent, 'live-output.log');
|
|
160
160
|
let lastActivity = item.started_at ? new Date(item.started_at).getTime() : 0;
|
|
161
161
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
// For tracked processes, use realActivityMap (tracks actual agent stdout/stderr only,
|
|
163
|
+
// NOT engine heartbeat writes). This prevents the feedback loop where engine heartbeat
|
|
164
|
+
// writes to live-output.log reset the mtime that the timeout check reads (#724).
|
|
165
|
+
const realActivityMap = engine().realActivityMap;
|
|
166
|
+
if (hasProcess && realActivityMap?.has(item.id)) {
|
|
167
|
+
lastActivity = Math.max(lastActivity, realActivityMap.get(item.id));
|
|
168
|
+
} else {
|
|
169
|
+
// Orphan case (no tracked process): use live-output.log mtime as fallback.
|
|
170
|
+
// No heartbeat timer is running for orphans, so mtime is accurate.
|
|
171
|
+
try {
|
|
172
|
+
const stat = fs.statSync(liveLogPath);
|
|
173
|
+
lastActivity = Math.max(lastActivity, stat.mtimeMs);
|
|
174
|
+
} catch { /* optional */ }
|
|
175
|
+
}
|
|
167
176
|
|
|
168
177
|
const silentMs = Date.now() - lastActivity;
|
|
169
178
|
const silentSec = Math.round(silentMs / 1000);
|
package/engine.js
CHANGED
|
@@ -142,6 +142,7 @@ const { runPostCompletionHooks, updateWorkItemStatus, syncPrdItemStatus, handleP
|
|
|
142
142
|
// ─── Agent Spawner ──────────────────────────────────────────────────────────
|
|
143
143
|
|
|
144
144
|
const activeProcesses = new Map(); // dispatchId → { proc, agentId, startedAt }
|
|
145
|
+
const realActivityMap = new Map(); // dispatchId → timestamp of last REAL agent output (not engine heartbeat)
|
|
145
146
|
// tempAgents imported from engine/routing.js
|
|
146
147
|
let engineRestartGraceUntil = 0; // timestamp — suppress orphan detection until this time
|
|
147
148
|
|
|
@@ -597,6 +598,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
597
598
|
proc.stdout.on('data', (data) => {
|
|
598
599
|
const chunk = data.toString();
|
|
599
600
|
lastOutputAt = Date.now();
|
|
601
|
+
realActivityMap.set(id, Date.now()); // Track real agent output separately from heartbeat
|
|
600
602
|
if (stdout.length < MAX_OUTPUT) stdout += chunk.slice(0, MAX_OUTPUT - stdout.length);
|
|
601
603
|
try { fs.appendFileSync(liveOutputPath, chunk); } catch { /* optional */ }
|
|
602
604
|
|
|
@@ -641,6 +643,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
641
643
|
proc.stderr.on('data', (data) => {
|
|
642
644
|
const chunk = data.toString();
|
|
643
645
|
lastOutputAt = Date.now();
|
|
646
|
+
realActivityMap.set(id, Date.now()); // Track real agent output separately from heartbeat
|
|
644
647
|
if (stderr.length < MAX_OUTPUT) stderr += chunk.slice(0, MAX_OUTPUT - stderr.length);
|
|
645
648
|
try { fs.appendFileSync(liveOutputPath, '[stderr] ' + chunk); } catch { /* optional */ }
|
|
646
649
|
});
|
|
@@ -737,12 +740,14 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
737
740
|
resumeProc.stdout.on('data', (data) => {
|
|
738
741
|
const chunk = data.toString();
|
|
739
742
|
lastOutputAt = Date.now();
|
|
743
|
+
realActivityMap.set(id, Date.now()); // Track real agent output separately from heartbeat
|
|
740
744
|
if (stdout.length < MAX_OUTPUT) stdout += chunk.slice(0, MAX_OUTPUT - stdout.length);
|
|
741
745
|
try { fs.appendFileSync(liveOutputPath, chunk); } catch { /* optional */ }
|
|
742
746
|
});
|
|
743
747
|
resumeProc.stderr.on('data', (data) => {
|
|
744
748
|
const chunk = data.toString();
|
|
745
749
|
lastOutputAt = Date.now();
|
|
750
|
+
realActivityMap.set(id, Date.now()); // Track real agent output separately from heartbeat
|
|
746
751
|
if (stderr.length < MAX_OUTPUT) stderr += chunk.slice(0, MAX_OUTPUT - stderr.length);
|
|
747
752
|
try { fs.appendFileSync(liveOutputPath, '[stderr] ' + chunk); } catch { /* optional */ }
|
|
748
753
|
});
|
|
@@ -773,6 +778,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
773
778
|
}
|
|
774
779
|
|
|
775
780
|
activeProcesses.delete(id);
|
|
781
|
+
realActivityMap.delete(id); // Clean up real activity tracking
|
|
776
782
|
|
|
777
783
|
// If timeout checker already finalized this dispatch, don't overwrite work-item status again.
|
|
778
784
|
// This avoids races where close-handler marks an auto-retried item as failed.
|
|
@@ -880,6 +886,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
880
886
|
if (heartbeatTimer) { clearInterval(heartbeatTimer); heartbeatTimer = null; }
|
|
881
887
|
log('error', `Failed to spawn agent ${agentId}: ${err.message}`);
|
|
882
888
|
activeProcesses.delete(id);
|
|
889
|
+
realActivityMap.delete(id); // Clean up real activity tracking
|
|
883
890
|
completeDispatch(id, DISPATCH_RESULT.ERROR, `Spawn error: ${err.message}`);
|
|
884
891
|
});
|
|
885
892
|
|
|
@@ -892,6 +899,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
892
899
|
|
|
893
900
|
// Track process — even if PID isn't available yet (async on Windows)
|
|
894
901
|
activeProcesses.set(id, { proc, agentId, startedAt, sessionId: cachedSessionId });
|
|
902
|
+
realActivityMap.set(id, Date.now()); // Initialize real activity at spawn time
|
|
895
903
|
|
|
896
904
|
updateAgentStatus(id, AGENT_STATUS.RUNNING, `Process spawned for ${agentId}`);
|
|
897
905
|
|
|
@@ -2957,7 +2965,7 @@ module.exports = {
|
|
|
2957
2965
|
|
|
2958
2966
|
// Dispatch management (re-exported from engine/dispatch.js)
|
|
2959
2967
|
mutateDispatch, addToDispatch, isRetryableFailureReason, completeDispatch, writeInboxAlert, updateAgentStatus,
|
|
2960
|
-
activeProcesses, get engineRestartGraceUntil() { return engineRestartGraceUntil; },
|
|
2968
|
+
activeProcesses, realActivityMap, get engineRestartGraceUntil() { return engineRestartGraceUntil; },
|
|
2961
2969
|
set engineRestartGraceUntil(v) { engineRestartGraceUntil = v; },
|
|
2962
2970
|
|
|
2963
2971
|
// Agent lifecycle
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.739",
|
|
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"
|