@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 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
@@ -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 (last hour) to prevent re-dispatch
99
- const oneHourAgo = Date.now() - 3600000;
100
- const recentCompleted = (dispatch.completed || []).filter(d =>
101
- d.completed_at && new Date(d.completed_at).getTime() > oneHourAgo
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
- if (Date.now() - steerMtime > 300000) {
70
- log('warn', `Steering: no sessionId for ${info.agentId} after 5m deleting stale message`);
71
- try { fs.unlinkSync(steerPath); } catch {}
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 default to 10min for any Bash call
191
- const bashTimeout = input.timeout || 600000;
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.667",
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"