@yemi33/minions 0.1.2121 → 0.1.2123
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/dashboard/js/settings.js +4 -0
- package/dashboard.js +3 -0
- package/docs/harness-mode.md +92 -0
- package/engine/ado.js +142 -21
- package/engine/github.js +4 -1
- package/engine/harness.js +592 -0
- package/engine/lifecycle.js +91 -0
- package/engine/scheduler.js +40 -3
- package/engine/shared.js +16 -0
- package/engine/timeout.js +286 -21
- package/engine.js +66 -15
- package/package.json +1 -1
package/engine.js
CHANGED
|
@@ -123,7 +123,7 @@ const { mutateDispatch, addToDispatch, addToDispatchWithValidation, isRetryableF
|
|
|
123
123
|
|
|
124
124
|
// ─── Timeout / Steering / Idle (extracted to engine/timeout.js) ──────────────
|
|
125
125
|
|
|
126
|
-
const { checkTimeouts, checkSteering, checkIdleThreshold } = require('./engine/timeout');
|
|
126
|
+
const { checkTimeouts, checkSteering, checkIdleThreshold, dropSteeringForPurgedSession } = require('./engine/timeout');
|
|
127
127
|
const steering = require('./engine/steering');
|
|
128
128
|
|
|
129
129
|
// ─── Cleanup (extracted to engine/cleanup.js) ────────────────────────────────
|
|
@@ -528,6 +528,11 @@ function promoteCheckpointSteeringForClose(agentId, procInfo, runtime, liveOutpu
|
|
|
528
528
|
const checkpointEntries = mergePendingSteeringEntries(pendingDeferred, lateCheckpoint);
|
|
529
529
|
if (checkpointEntries.length === 0) {
|
|
530
530
|
delete procInfo._deferredSteeringFiles;
|
|
531
|
+
// Gap B housekeeping: drop the per-entry deferred timestamps + stranded
|
|
532
|
+
// guard so a future spawn under the same procInfo (test harnesses + engine
|
|
533
|
+
// re-attach) starts with a clean slate.
|
|
534
|
+
delete procInfo._deferredSteeringQueuedAt;
|
|
535
|
+
delete procInfo._deferredSteeringStrandedFiles;
|
|
531
536
|
return { status: 'none', entries: [] };
|
|
532
537
|
}
|
|
533
538
|
|
|
@@ -548,6 +553,8 @@ function promoteCheckpointSteeringForClose(agentId, procInfo, runtime, liveOutpu
|
|
|
548
553
|
procInfo._steeringEntry = checkpointEntries;
|
|
549
554
|
procInfo._steeringDeferredCheckpoint = true;
|
|
550
555
|
delete procInfo._deferredSteeringFiles;
|
|
556
|
+
delete procInfo._deferredSteeringQueuedAt;
|
|
557
|
+
delete procInfo._deferredSteeringStrandedFiles;
|
|
551
558
|
// W-mq066js7000fff1f-a (Gap D): transition each promoted entry to
|
|
552
559
|
// 're_spawning' — captures that the engine has committed to deliver
|
|
553
560
|
// these messages via session resume at the natural checkpoint.
|
|
@@ -2683,9 +2690,14 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
2683
2690
|
updateAgentStatus(id, code === 0 ? AGENT_STATUS.FINISHED : AGENT_STATUS.FAILED,
|
|
2684
2691
|
code === 0 ? 'Agent completed successfully' : `Agent exited with code ${code}`);
|
|
2685
2692
|
|
|
2686
|
-
// Clear stale session if resume failed — prevents burning all retries on the same bad session
|
|
2693
|
+
// Clear stale session if resume failed — prevents burning all retries on the same bad session.
|
|
2694
|
+
// W-mq066js7000fff1f-c (Gap G): drop any in-flight steering-store entries
|
|
2695
|
+
// bound to the purged session BEFORE unlinking session.json, so the human
|
|
2696
|
+
// sees a [steering-failed] line + can re-send instead of silently watching
|
|
2697
|
+
// their message strand against a dead session.
|
|
2687
2698
|
if (code !== 0 && cachedSessionId && stderr.includes('No conversation found')) {
|
|
2688
2699
|
log('warn', `Stale session ${cachedSessionId} for ${agentId} — clearing session.json`);
|
|
2700
|
+
try { dropSteeringForPurgedSession(agentId, cachedSessionId, liveOutputPath); } catch {}
|
|
2689
2701
|
try { shared.safeUnlink(path.join(AGENTS_DIR, agentId, 'session.json')); } catch {}
|
|
2690
2702
|
}
|
|
2691
2703
|
|
|
@@ -3932,7 +3944,7 @@ function reconcileItemsWithPrs(items, allPrs, { onlyIds } = {}) {
|
|
|
3932
3944
|
// ─── Inbox Consolidation (extracted to engine/consolidation.js) ──────────────
|
|
3933
3945
|
|
|
3934
3946
|
const { consolidateInbox } = require('./engine/consolidation');
|
|
3935
|
-
const { pollPrStatus, pollPrHumanComments, reconcilePrs, checkLiveReviewStatus: adoCheckLiveReview, checkLiveBuildAndConflict: adoCheckLiveBuildAndConflict, needsAdoPollRetry, getAdoToken, isAdoThrottled } = require('./engine/ado');
|
|
3947
|
+
const { pollPrStatus, pollPrHumanComments, reconcilePrs, checkLiveReviewStatus: adoCheckLiveReview, checkLiveBuildAndConflict: adoCheckLiveBuildAndConflict, needsAdoPollRetry, getAdoToken, isAdoThrottled, getAdoThrottleStateAll } = require('./engine/ado');
|
|
3936
3948
|
const { pollPrStatus: ghPollPrStatus, pollPrHumanComments: ghPollPrHumanComments, reconcilePrs: ghReconcilePrs, checkLiveReviewStatus: ghCheckLiveReview, checkLiveBuildAndConflict: ghCheckLiveBuildAndConflict, isGhThrottled } = require('./engine/github');
|
|
3937
3949
|
|
|
3938
3950
|
// ─── State Snapshot ─────────────────────────────────────────────────────────
|
|
@@ -6866,12 +6878,35 @@ async function discoverWork(config) {
|
|
|
6866
6878
|
mutateJsonFileLocked(centralPath, (items) => {
|
|
6867
6879
|
if (!Array.isArray(items)) items = [];
|
|
6868
6880
|
let added = 0;
|
|
6881
|
+
// Snapshot active dedup keys BEFORE the loop so multiple items in the
|
|
6882
|
+
// same harness mission (same _missionId) all land in one tick. Without
|
|
6883
|
+
// this snapshot, the first item's push would block subsequent items
|
|
6884
|
+
// in the same mission from joining (W-mq07a9gf000jbc2b — tri-agent
|
|
6885
|
+
// harness mode requires Planner+Generator+Evaluator to land together).
|
|
6886
|
+
const activeMissionIds = new Set();
|
|
6887
|
+
const activeScheduleIds = new Set();
|
|
6888
|
+
for (const existing of items) {
|
|
6889
|
+
if (existing.status === WI_STATUS.DONE || existing.status === WI_STATUS.FAILED) continue;
|
|
6890
|
+
if (existing._missionId) activeMissionIds.add(existing._missionId);
|
|
6891
|
+
if (existing._scheduleId) activeScheduleIds.add(existing._scheduleId);
|
|
6892
|
+
}
|
|
6893
|
+
const addedScheduleIdsThisTick = new Set();
|
|
6869
6894
|
for (const item of taskItems) {
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6895
|
+
// Mission items dedup by _missionId against pre-existing rows only
|
|
6896
|
+
// (the trio's other items added later in this loop must not block
|
|
6897
|
+
// each other). Plain scheduled items keep the original scheduleId
|
|
6898
|
+
// dedup AND skip if a sibling item from the same tick already
|
|
6899
|
+
// claimed the schedule slot.
|
|
6900
|
+
if (item._missionId) {
|
|
6901
|
+
if (activeMissionIds.has(item._missionId)) continue;
|
|
6902
|
+
} else {
|
|
6903
|
+
if (activeScheduleIds.has(item._scheduleId)) continue;
|
|
6904
|
+
if (addedScheduleIdsThisTick.has(item._scheduleId)) continue;
|
|
6874
6905
|
}
|
|
6906
|
+
items.push(item);
|
|
6907
|
+
if (!item._missionId && item._scheduleId) addedScheduleIdsThisTick.add(item._scheduleId);
|
|
6908
|
+
added++;
|
|
6909
|
+
log('info', `Scheduled task fired: ${item._scheduleId} → ${item.title}`);
|
|
6875
6910
|
}
|
|
6876
6911
|
return items;
|
|
6877
6912
|
}, { defaultValue: [] });
|
|
@@ -7337,10 +7372,18 @@ async function tickInner() {
|
|
|
7337
7372
|
lastPrStatusPollAt = now;
|
|
7338
7373
|
// Build promise array — enabled+unthrottled polls run concurrently via Promise.allSettled
|
|
7339
7374
|
const statusPolls = [];
|
|
7340
|
-
if (adoPollEnabled
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
log
|
|
7375
|
+
if (adoPollEnabled) {
|
|
7376
|
+
// Per-org throttle skip happens inside forEachActivePr (one log line per skipped project).
|
|
7377
|
+
// Top-level short-circuit: when every known ADO org is throttled, skip the whole phase
|
|
7378
|
+
// with one log line to avoid the per-project iteration cost.
|
|
7379
|
+
const adoThrottleStates = getAdoThrottleStateAll() || {};
|
|
7380
|
+
const adoOrgCount = Object.keys(adoThrottleStates).length;
|
|
7381
|
+
const allAdoThrottled = adoOrgCount > 0 && Object.values(adoThrottleStates).every(s => s && s.throttled);
|
|
7382
|
+
if (allAdoThrottled) {
|
|
7383
|
+
log('info', `[ado] PR status poll skipped — all ${adoOrgCount} known orgs throttled`);
|
|
7384
|
+
} else {
|
|
7385
|
+
statusPolls.push(pollPrStatus(config).catch(err => { log('warn', `ADO PR status poll error: ${err?.message || err}${err?.stack ? ' | ' + err.stack.split('\n')[1]?.trim() : ''}`); }));
|
|
7386
|
+
}
|
|
7344
7387
|
}
|
|
7345
7388
|
if (ghPollEnabled && !isGhThrottled()) {
|
|
7346
7389
|
statusPolls.push(ghPollPrStatus(config).catch(err => { log('warn', `GitHub PR status poll error: ${err?.message || err}${err?.stack ? ' | ' + err.stack.split('\n')[1]?.trim() : ''}`); }));
|
|
@@ -7383,10 +7426,18 @@ async function tickInner() {
|
|
|
7383
7426
|
lastPrCommentsPollAt = now;
|
|
7384
7427
|
// Build promise array — enabled+unthrottled comment polls run concurrently via Promise.allSettled
|
|
7385
7428
|
const commentPolls = [];
|
|
7386
|
-
if (adoPollEnabled
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
log
|
|
7429
|
+
if (adoPollEnabled) {
|
|
7430
|
+
// Per-org throttle skip happens inside forEachActivePr (one log line per skipped project).
|
|
7431
|
+
// Top-level short-circuit: when every known ADO org is throttled, skip the whole phase
|
|
7432
|
+
// with one log line to avoid the per-project iteration cost.
|
|
7433
|
+
const adoThrottleStates = getAdoThrottleStateAll() || {};
|
|
7434
|
+
const adoOrgCount = Object.keys(adoThrottleStates).length;
|
|
7435
|
+
const allAdoThrottled = adoOrgCount > 0 && Object.values(adoThrottleStates).every(s => s && s.throttled);
|
|
7436
|
+
if (allAdoThrottled) {
|
|
7437
|
+
log('info', `[ado] PR comment poll skipped — all ${adoOrgCount} known orgs throttled`);
|
|
7438
|
+
} else {
|
|
7439
|
+
commentPolls.push(pollPrHumanComments(config).catch(err => { log('warn', `ADO PR comment poll error: ${err?.message || err}${err?.stack ? ' | ' + err.stack.split('\n')[1]?.trim() : ''}`); }));
|
|
7440
|
+
}
|
|
7390
7441
|
}
|
|
7391
7442
|
if (ghPollEnabled && !isGhThrottled()) {
|
|
7392
7443
|
commentPolls.push(ghPollPrHumanComments(config).catch(err => { log('warn', `GitHub PR comment poll error: ${err?.message || err}${err?.stack ? ' | ' + err.stack.split('\n')[1]?.trim() : ''}`); }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2123",
|
|
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"
|