@yemi33/minions 0.1.1627 → 0.1.1628
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 +4 -1
- package/engine/copilot-models.json +1 -1
- package/engine/dispatch.js +68 -3
- package/engine.js +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/engine/dispatch.js
CHANGED
|
@@ -12,7 +12,7 @@ const { setCooldownFailure } = require('./cooldown');
|
|
|
12
12
|
const { safeJson, safeWrite, safeReadDir, mutateJsonFileLocked, mutateWorkItems,
|
|
13
13
|
mutatePullRequests, getProjects, projectWorkItemsPath, projectPrPath, log, ts, dateStamp,
|
|
14
14
|
sidecarDispatchPrompt, deleteDispatchPromptSidecar,
|
|
15
|
-
WI_STATUS, DISPATCH_RESULT, ENGINE_DEFAULTS, AGENT_STATUS, FAILURE_CLASS } = shared;
|
|
15
|
+
WI_STATUS, DISPATCH_RESULT, ENGINE_DEFAULTS, AGENT_STATUS, FAILURE_CLASS, PR_STATUS } = shared;
|
|
16
16
|
const { getConfig, getDispatch, DISPATCH_PATH, INBOX_DIR } = queries;
|
|
17
17
|
|
|
18
18
|
const MINIONS_DIR = shared.MINIONS_DIR;
|
|
@@ -91,6 +91,64 @@ function addToDispatch(item) {
|
|
|
91
91
|
return item.id;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
function _resolveDispatchProject(projectRef, config) {
|
|
95
|
+
if (!projectRef) return null;
|
|
96
|
+
const projects = getProjects(config);
|
|
97
|
+
if (projectRef.name) {
|
|
98
|
+
const byName = projects.find(p => p.name === projectRef.name);
|
|
99
|
+
if (byName) return byName;
|
|
100
|
+
}
|
|
101
|
+
if (projectRef.localPath) {
|
|
102
|
+
const refPath = path.resolve(projectRef.localPath);
|
|
103
|
+
const byPath = projects.find(p => p.localPath && path.resolve(p.localPath) === refPath);
|
|
104
|
+
if (byPath) return byPath;
|
|
105
|
+
}
|
|
106
|
+
return projectRef;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function _isPrBackedDispatch(entry) {
|
|
110
|
+
return !!(entry?.meta?.pr && entry.meta?.project);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getStalePrDispatchReason(entry, config) {
|
|
114
|
+
if (!_isPrBackedDispatch(entry)) return '';
|
|
115
|
+
const project = _resolveDispatchProject(entry.meta.project, config);
|
|
116
|
+
if (!project) return 'missing project metadata';
|
|
117
|
+
|
|
118
|
+
const tracked = shared.findPrRecord(queries.getPrs(project), entry.meta.pr, project);
|
|
119
|
+
const prLabel = entry.meta.pr?.id || entry.meta.pr?.url || entry.id;
|
|
120
|
+
if (!tracked) return `PR ${prLabel} is no longer tracked`;
|
|
121
|
+
if (tracked.status !== PR_STATUS.ACTIVE) return `PR ${tracked.id || prLabel} is ${tracked.status || 'missing status'}`;
|
|
122
|
+
if (tracked._contextOnly) return `PR ${tracked.id || prLabel} is context-only`;
|
|
123
|
+
|
|
124
|
+
const queuedBranch = entry.meta.branch || entry.meta.pr?.branch || '';
|
|
125
|
+
const trackedBranch = tracked.branch || '';
|
|
126
|
+
if (queuedBranch && trackedBranch && shared.sanitizeBranch(queuedBranch) !== shared.sanitizeBranch(trackedBranch)) {
|
|
127
|
+
return `PR ${tracked.id || prLabel} branch changed from ${queuedBranch} to ${trackedBranch}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return '';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function pruneStalePrDispatches(config = queries.getConfig()) {
|
|
134
|
+
const removed = [];
|
|
135
|
+
mutateDispatch((dispatch) => {
|
|
136
|
+
dispatch.pending = (dispatch.pending || []).filter(entry => {
|
|
137
|
+
const reason = getStalePrDispatchReason(entry, config);
|
|
138
|
+
if (!reason) return true;
|
|
139
|
+
removed.push({ entry, reason });
|
|
140
|
+
return false;
|
|
141
|
+
});
|
|
142
|
+
return dispatch;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
for (const { entry, reason } of removed) {
|
|
146
|
+
try { deleteDispatchPromptSidecar(entry); } catch { /* cleanup best-effort */ }
|
|
147
|
+
log('info', `Dropped stale PR dispatch ${entry.id}: ${reason}`);
|
|
148
|
+
}
|
|
149
|
+
return removed.length;
|
|
150
|
+
}
|
|
151
|
+
|
|
94
152
|
// ─── Retryable Failure Classification ────────────────────────────────────────
|
|
95
153
|
|
|
96
154
|
function isRetryableFailureReason(reason = '', failureClass = '') {
|
|
@@ -253,11 +311,16 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
|
|
|
253
311
|
if (prId && project) {
|
|
254
312
|
try {
|
|
255
313
|
const prsPath = projectPrPath(project);
|
|
314
|
+
let restored = false;
|
|
256
315
|
mutatePullRequests(prsPath, prs => {
|
|
257
316
|
const target = shared.findPrRecord(prs, { id: prId }, project);
|
|
258
|
-
if (target?.humanFeedback)
|
|
317
|
+
if (target?.humanFeedback) {
|
|
318
|
+
target.humanFeedback.pendingFix = true;
|
|
319
|
+
restored = true;
|
|
320
|
+
}
|
|
259
321
|
});
|
|
260
|
-
log('info', `Restored pendingFix=true on ${prId} after failed human-feedback fix`);
|
|
322
|
+
if (restored) log('info', `Restored pendingFix=true on ${prId} after failed human-feedback fix`);
|
|
323
|
+
else log('info', `Skipped pendingFix restore for ${prId} — PR is no longer tracked`);
|
|
261
324
|
} catch (e) { log('warn', `restore pendingFix: ${e.message}`); }
|
|
262
325
|
}
|
|
263
326
|
// Clear completed dispatch entry so dedup doesn't block re-dispatch
|
|
@@ -424,6 +487,8 @@ module.exports = {
|
|
|
424
487
|
completeDispatch,
|
|
425
488
|
writeInboxAlert,
|
|
426
489
|
updateAgentStatus,
|
|
490
|
+
getStalePrDispatchReason,
|
|
491
|
+
pruneStalePrDispatches,
|
|
427
492
|
cancelPendingDispatchesForPr,
|
|
428
493
|
cleanDispatchEntries,
|
|
429
494
|
cancelPendingWorkItems,
|
package/engine.js
CHANGED
|
@@ -102,7 +102,7 @@ const withFileLock = shared.withFileLock;
|
|
|
102
102
|
// ─── Dispatch Management (extracted to engine/dispatch.js) ───────────────────
|
|
103
103
|
|
|
104
104
|
const { mutateDispatch, addToDispatch, isRetryableFailureReason, completeDispatch,
|
|
105
|
-
writeInboxAlert, updateAgentStatus } = require('./engine/dispatch');
|
|
105
|
+
writeInboxAlert, updateAgentStatus, pruneStalePrDispatches } = require('./engine/dispatch');
|
|
106
106
|
|
|
107
107
|
// ─── Timeout / Steering / Idle (extracted to engine/timeout.js) ──────────────
|
|
108
108
|
|
|
@@ -3639,6 +3639,7 @@ async function tickInner() {
|
|
|
3639
3639
|
const maxC = config.engine?.maxConcurrent ?? ENGINE_DEFAULTS.maxConcurrent;
|
|
3640
3640
|
setTempBudget(Math.max(0, maxC - activeCountPre));
|
|
3641
3641
|
}
|
|
3642
|
+
try { pruneStalePrDispatches(config); } catch (e) { log('warn', 'prune stale PR dispatches: ' + e.message); }
|
|
3642
3643
|
let discoveryOk = true;
|
|
3643
3644
|
try { await discoverWork(config); } catch (e) { log('warn', 'discoverWork: ' + e.message); discoveryOk = false; }
|
|
3644
3645
|
|
|
@@ -3915,7 +3916,7 @@ module.exports = {
|
|
|
3915
3916
|
validateConfig,
|
|
3916
3917
|
|
|
3917
3918
|
// Dispatch management (re-exported from engine/dispatch.js)
|
|
3918
|
-
mutateDispatch, addToDispatch, isRetryableFailureReason, completeDispatch, writeInboxAlert, updateAgentStatus,
|
|
3919
|
+
mutateDispatch, addToDispatch, isRetryableFailureReason, completeDispatch, writeInboxAlert, updateAgentStatus, pruneStalePrDispatches,
|
|
3919
3920
|
activeProcesses, realActivityMap, engineRestartGraceExempt,
|
|
3920
3921
|
get engineRestartGraceUntil() { return engineRestartGraceUntil; },
|
|
3921
3922
|
set engineRestartGraceUntil(v) { engineRestartGraceUntil = v; },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1628",
|
|
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"
|