@yemi33/minions 0.1.499 → 0.1.501
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 -1
- package/engine.js +43 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.501 (2026-04-07)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
|
+
- explicitly-assigned work items bypass concurrency cap
|
|
6
7
|
- strengthen CC dispatch contract with domain terminology mapping
|
|
7
8
|
|
|
9
|
+
### Fixes
|
|
10
|
+
- comprehensive steering failure handling with user-visible feedback
|
|
11
|
+
|
|
8
12
|
## 0.1.498 (2026-04-07)
|
|
9
13
|
|
|
10
14
|
### Features
|
package/engine.js
CHANGED
|
@@ -576,12 +576,27 @@ function spawnAgent(dispatchItem, config) {
|
|
|
576
576
|
delete procInfo._steeringMessage;
|
|
577
577
|
delete procInfo._steeringSessionId;
|
|
578
578
|
|
|
579
|
+
// Guard: can't resume without a session
|
|
580
|
+
if (!steerSessionId) {
|
|
581
|
+
log('warn', `Steering: no sessionId for ${agentId} — appending message to live output only`);
|
|
582
|
+
try { fs.appendFileSync(liveOutputPath, `\n[steering-failed] No session to resume. Message was: ${steerMsg}\n`); } catch {}
|
|
583
|
+
activeProcesses.delete(id);
|
|
584
|
+
completeDispatch(id, DISPATCH_RESULT.SUCCESS, 'Steering skipped (no session)', '', { processWorkItemFailure: false });
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
579
588
|
log('info', `Steering: re-spawning ${agentId} with --resume ${steerSessionId}`);
|
|
580
589
|
|
|
581
590
|
// Write new prompt with steering message
|
|
582
591
|
const steerPrompt = `Message from your human teammate:\n\n${steerMsg}\n\nRespond to this, then continue working on your current task.`;
|
|
583
592
|
const steerPromptPath = path.join(ENGINE_DIR, 'tmp', `prompt-steer-${safeId}.md`);
|
|
584
|
-
safeWrite(steerPromptPath, steerPrompt);
|
|
593
|
+
try { safeWrite(steerPromptPath, steerPrompt); } catch (e) {
|
|
594
|
+
log('warn', `Steering: failed to write prompt for ${agentId}: ${e.message}`);
|
|
595
|
+
try { fs.appendFileSync(liveOutputPath, `\n[steering-failed] Could not write prompt. Message was: ${steerMsg}\n`); } catch {}
|
|
596
|
+
activeProcesses.delete(id);
|
|
597
|
+
completeDispatch(id, DISPATCH_RESULT.SUCCESS, 'Steering prompt write failed', '', { processWorkItemFailure: false });
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
585
600
|
|
|
586
601
|
// Build resume args
|
|
587
602
|
const resumeArgs = [
|
|
@@ -595,11 +610,21 @@ function spawnAgent(dispatchItem, config) {
|
|
|
595
610
|
|
|
596
611
|
const spawnScript = path.join(ENGINE_DIR, 'spawn-agent.js');
|
|
597
612
|
const childEnv = shared.cleanChildEnv();
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
613
|
+
let resumeProc;
|
|
614
|
+
try {
|
|
615
|
+
resumeProc = runFile(process.execPath, [spawnScript, steerPromptPath, steerPromptPath, ...resumeArgs], {
|
|
616
|
+
cwd,
|
|
617
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
618
|
+
env: childEnv,
|
|
619
|
+
});
|
|
620
|
+
} catch (e) {
|
|
621
|
+
log('warn', `Steering: spawn failed for ${agentId}: ${e.message}`);
|
|
622
|
+
try { fs.appendFileSync(liveOutputPath, `\n[steering-failed] Spawn error: ${e.message}. Message was: ${steerMsg}\n`); } catch {}
|
|
623
|
+
try { fs.unlinkSync(steerPromptPath); } catch {}
|
|
624
|
+
activeProcesses.delete(id);
|
|
625
|
+
completeDispatch(id, DISPATCH_RESULT.SUCCESS, 'Steering spawn failed', '', { processWorkItemFailure: false });
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
603
628
|
|
|
604
629
|
// Re-attach to existing tracking
|
|
605
630
|
activeProcesses.set(id, { proc: resumeProc, agentId, startedAt: procInfo.startedAt, sessionId: steerSessionId, _steeringAt: Date.now() });
|
|
@@ -635,6 +660,7 @@ function spawnAgent(dispatchItem, config) {
|
|
|
635
660
|
try { fs.unlinkSync(steerPromptPath); } catch { /* cleanup */ }
|
|
636
661
|
if (resumeCode !== 0) {
|
|
637
662
|
log('warn', `Steering resume for ${agentId} exited with code ${resumeCode} | stderr: ${stderr.slice(-300).replace(/\n/g, ' ')}`);
|
|
663
|
+
try { fs.appendFileSync(liveOutputPath, `\n[steering-failed] Resume exited with code ${resumeCode}. Your message was received but the agent could not continue the session.\n`); } catch {}
|
|
638
664
|
activeProcesses.delete(id);
|
|
639
665
|
completeDispatch(id, DISPATCH_RESULT.SUCCESS, 'Steering resume failed but original work completed', '', { processWorkItemFailure: false });
|
|
640
666
|
return;
|
|
@@ -644,6 +670,7 @@ function spawnAgent(dispatchItem, config) {
|
|
|
644
670
|
});
|
|
645
671
|
resumeProc.on('error', (err) => {
|
|
646
672
|
log('warn', `Steering re-spawn error for ${agentId}: ${err.message}`);
|
|
673
|
+
try { fs.appendFileSync(liveOutputPath, `\n[steering-failed] Spawn error: ${err.message}. Your message was received but the agent could not resume.\n`); } catch {}
|
|
647
674
|
activeProcesses.delete(id);
|
|
648
675
|
completeDispatch(id, DISPATCH_RESULT.SUCCESS, 'Steering re-spawn error but original work completed', '', { processWorkItemFailure: false });
|
|
649
676
|
});
|
|
@@ -2446,12 +2473,7 @@ async function tickInner() {
|
|
|
2446
2473
|
const activeCount = (dispatch.active || []).length;
|
|
2447
2474
|
const maxConcurrent = config.engine?.maxConcurrent || 5;
|
|
2448
2475
|
|
|
2449
|
-
|
|
2450
|
-
log('info', `At max concurrency (${activeCount}/${maxConcurrent}) — skipping dispatch`);
|
|
2451
|
-
return;
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
const slotsAvailable = maxConcurrent - activeCount;
|
|
2476
|
+
const slotsAvailable = Math.max(0, maxConcurrent - activeCount);
|
|
2455
2477
|
|
|
2456
2478
|
// Priority dispatch: fixes > reviews > plan-to-prd > implement > verify > other
|
|
2457
2479
|
const typePriority = { 'implement:large': 0, implement: 0, fix: 1, ask: 1, review: 2, test: 3, verify: 3, plan: 4, 'plan-to-prd': 4 };
|
|
@@ -2468,23 +2490,25 @@ async function tickInner() {
|
|
|
2468
2490
|
return dp;
|
|
2469
2491
|
});
|
|
2470
2492
|
|
|
2471
|
-
//
|
|
2472
|
-
// Build set of agents currently active.
|
|
2493
|
+
// Build set of agents currently active (one task per agent at a time).
|
|
2473
2494
|
const busyAgents = new Set((dispatch.active || []).map(d => d.agent));
|
|
2474
|
-
// Bug fix #14: deduplicate pending by dispatch ID to prevent double-dispatch.
|
|
2475
|
-
// This guards against the same item appearing twice in the in-memory pending array.
|
|
2476
2495
|
const seenPendingIds = new Set();
|
|
2477
2496
|
const toDispatch = [];
|
|
2497
|
+
let generalSlots = slotsAvailable;
|
|
2498
|
+
|
|
2478
2499
|
for (const item of dispatch.pending) {
|
|
2479
|
-
if (toDispatch.length >= slotsAvailable) break;
|
|
2480
2500
|
if (seenPendingIds.has(item.id)) {
|
|
2481
2501
|
log('warn', `Duplicate dispatch ID ${item.id} in pending queue — skipping`);
|
|
2482
2502
|
continue;
|
|
2483
2503
|
}
|
|
2504
|
+
if (busyAgents.has(item.agent)) continue;
|
|
2505
|
+
// Items explicitly assigned to an agent bypass concurrency cap — dispatch if agent is free
|
|
2506
|
+
const isExplicitAssignment = !!item.meta?.item?.agent;
|
|
2507
|
+
if (!isExplicitAssignment && generalSlots <= 0) continue;
|
|
2484
2508
|
seenPendingIds.add(item.id);
|
|
2485
|
-
if (busyAgents.has(item.agent)) continue; // agent already has an active task
|
|
2486
2509
|
toDispatch.push(item);
|
|
2487
|
-
busyAgents.add(item.agent);
|
|
2510
|
+
busyAgents.add(item.agent);
|
|
2511
|
+
if (!isExplicitAssignment) generalSlots--;
|
|
2488
2512
|
}
|
|
2489
2513
|
|
|
2490
2514
|
// Dispatch items — spawnAgent moves each from pending→active on disk.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.501",
|
|
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"
|