@yemi33/minions 0.1.667 → 0.1.669
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 +6 -0
- package/engine/cooldown.js +7 -5
- package/engine/timeout.js +44 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.669 (2026-04-09)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- interrupt blocking tool calls immediately on steering (closes #627) (#632)
|
|
7
|
+
- reduce Bash blocking grace and errored task dedup window (closes #593) (#630)
|
|
8
|
+
|
|
3
9
|
## 0.1.667 (2026-04-09)
|
|
4
10
|
|
|
5
11
|
### Features
|
package/engine/cooldown.js
CHANGED
|
@@ -95,11 +95,13 @@ function isAlreadyDispatched(key) {
|
|
|
95
95
|
// Check pending and active
|
|
96
96
|
const inFlight = [...dispatch.pending, ...(dispatch.active || [])];
|
|
97
97
|
if (inFlight.some(d => d.meta?.dispatchKey === key)) return true;
|
|
98
|
-
// Also check recently completed (
|
|
99
|
-
const
|
|
100
|
-
const recentCompleted = (dispatch.completed || []).filter(d =>
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
// Also check recently completed — shorter window for errors (15min) vs success (1hr)
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
const recentCompleted = (dispatch.completed || []).filter(d => {
|
|
101
|
+
if (!d.completed_at) return false;
|
|
102
|
+
const windowMs = d.result === 'error' ? 900000 : 3600000; // 15 min for errors, 1 hr for success
|
|
103
|
+
return now - new Date(d.completed_at).getTime() < windowMs;
|
|
104
|
+
});
|
|
103
105
|
return recentCompleted.some(d => d.meta?.dispatchKey === key);
|
|
104
106
|
}
|
|
105
107
|
|
package/engine/timeout.js
CHANGED
|
@@ -54,9 +54,28 @@ function checkIdleThreshold(config) {
|
|
|
54
54
|
|
|
55
55
|
// ─── Steering Checker ────────────────────────────────────────────────────────
|
|
56
56
|
|
|
57
|
+
// How long to wait for a steered agent to exit before retrying the kill
|
|
58
|
+
const STEERING_KILL_RETRY_MS = 30000;
|
|
59
|
+
|
|
57
60
|
function checkSteering(config) {
|
|
58
61
|
const activeProcesses = engine().activeProcesses;
|
|
59
62
|
for (const [id, info] of activeProcesses) {
|
|
63
|
+
// Recovery: if steering kill hasn't resulted in process exit within 30s, force-retry.
|
|
64
|
+
// This catches cases where killImmediate silently failed (e.g., orphaned subprocess
|
|
65
|
+
// on Unix where SIGKILL only hit spawn-agent.js, not the Claude CLI tree).
|
|
66
|
+
if (info._steeringAt && Date.now() - info._steeringAt > STEERING_KILL_RETRY_MS) {
|
|
67
|
+
if (!info._steeringRetried) {
|
|
68
|
+
log('warn', `Steering: ${info.agentId} (${id}) didn't exit ${STEERING_KILL_RETRY_MS / 1000}s after kill — retrying`);
|
|
69
|
+
shared.killImmediate(info.proc);
|
|
70
|
+
// On Unix, also try to kill children that may have been orphaned
|
|
71
|
+
if (process.platform !== 'win32' && info.proc?.pid) {
|
|
72
|
+
try { shared.exec(`pkill -KILL -P ${info.proc.pid}`, { timeout: 3000 }); } catch { /* children may already be dead */ }
|
|
73
|
+
}
|
|
74
|
+
info._steeringRetried = true;
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
60
79
|
// Skip if already being steered (prevents double-kill race)
|
|
61
80
|
if (info._steeringMessage || info._steeringAt) continue;
|
|
62
81
|
|
|
@@ -64,19 +83,34 @@ function checkSteering(config) {
|
|
|
64
83
|
let steerMtime;
|
|
65
84
|
try { steerMtime = fs.statSync(steerPath).mtimeMs; } catch { continue; } // ENOENT = no steering message
|
|
66
85
|
|
|
86
|
+
// Read and consume the message immediately — always delete to prevent stale messages
|
|
87
|
+
const message = safeRead(steerPath);
|
|
88
|
+
try { fs.unlinkSync(steerPath); } catch { /* cleanup */ }
|
|
89
|
+
if (!message) continue;
|
|
90
|
+
|
|
67
91
|
const sessionId = info.sessionId;
|
|
68
92
|
if (!sessionId) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
93
|
+
// No session to resume — kill agent and deliver message via inbox for retry.
|
|
94
|
+
// Previously this silently skipped for up to 5m then deleted the message (#627).
|
|
95
|
+
log('info', `Steering: no sessionId for ${info.agentId} (${id}) — killing and forwarding message to inbox`);
|
|
96
|
+
|
|
97
|
+
// Write steering message to agent inbox so it survives the retry
|
|
98
|
+
const inboxDir = path.join(AGENTS_DIR, info.agentId, 'inbox');
|
|
99
|
+
try { fs.mkdirSync(inboxDir, { recursive: true }); } catch {}
|
|
100
|
+
safeWrite(path.join(inboxDir, `steering-${Date.now()}.md`), `# Steering Message (Forwarded)\n\nOriginal steering from human:\n\n${message}\n`);
|
|
101
|
+
|
|
102
|
+
// Append to live output so user sees confirmation in the dashboard
|
|
103
|
+
try {
|
|
104
|
+
const liveLogPath = path.join(AGENTS_DIR, info.agentId, 'live-output.log');
|
|
105
|
+
fs.appendFileSync(liveLogPath, `\n[steering] Message received but no session to resume. Killing agent — your message will be delivered on retry.\n`);
|
|
106
|
+
} catch { /* optional */ }
|
|
107
|
+
|
|
108
|
+
shared.killImmediate(info.proc);
|
|
109
|
+
info._steeringAt = Date.now();
|
|
110
|
+
info._steeringNoSession = true;
|
|
73
111
|
continue;
|
|
74
112
|
}
|
|
75
113
|
|
|
76
|
-
const message = safeRead(steerPath);
|
|
77
|
-
try { fs.unlinkSync(steerPath); } catch { /* cleanup */ }
|
|
78
|
-
if (!message) continue;
|
|
79
|
-
|
|
80
114
|
log('info', `Steering: killing ${info.agentId} (${id}) for session resume with human message`);
|
|
81
115
|
|
|
82
116
|
shared.killImmediate(info.proc);
|
|
@@ -187,8 +221,8 @@ function checkTimeouts(config) {
|
|
|
187
221
|
}
|
|
188
222
|
// Bash tool call — may be running a long build/install with no stdout
|
|
189
223
|
if (name === 'Bash') {
|
|
190
|
-
// Use explicit timeout if set, otherwise
|
|
191
|
-
const bashTimeout = input.timeout ||
|
|
224
|
+
// Use explicit timeout if set, otherwise match Claude Code's actual Bash default (120s)
|
|
225
|
+
const bashTimeout = input.timeout || 120000;
|
|
192
226
|
blockingTimeout = Math.max(heartbeatTimeout, bashTimeout + 60000);
|
|
193
227
|
isBlocking = true;
|
|
194
228
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.669",
|
|
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"
|