@yemi33/minions 0.1.1692 → 0.1.1694
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 +7 -3
- package/README.md +1 -1
- package/dashboard/js/render-agents.js +0 -1
- package/dashboard/js/settings.js +4 -27
- package/dashboard.js +8 -5
- package/docs/auto-discovery.md +3 -1
- package/docs/blog-first-successful-dispatch.md +3 -3
- package/docs/deprecated.json +8 -0
- package/engine/copilot-models.json +1 -1
- package/engine/github.js +15 -7
- package/engine/lifecycle.js +60 -11
- package/engine/queries.js +1 -16
- package/engine/shared.js +17 -4
- package/minions.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.1694 (2026-05-04)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- remove stale permission mode settings (#2009)
|
|
7
|
+
- sync review verdict PR status (#2008)
|
|
8
|
+
|
|
9
|
+
## 0.1.1691 (2026-05-04)
|
|
4
10
|
|
|
5
11
|
### Features
|
|
6
|
-
- stop Azure auth for GitHub agents (#2005)
|
|
7
|
-
- recover stalled runtime resumes (#2004)
|
|
8
12
|
- preserve doc-chat response fragments (#2003)
|
|
9
13
|
|
|
10
14
|
## 0.1.1689 (2026-05-03)
|
package/README.md
CHANGED
|
@@ -406,7 +406,7 @@ No bash or shell involved — Node spawns Node directly. Dependency branches are
|
|
|
406
406
|
- **Working directory** — project root (agent creates worktrees as needed)
|
|
407
407
|
- **MCP servers** — inherited from `~/.claude.json` (no extra config needed)
|
|
408
408
|
- **Full tool access** — all built-in tools plus all MCP tools
|
|
409
|
-
- **Permission
|
|
409
|
+
- **Permission bypass** — runtime-owned; Claude uses `--dangerously-skip-permissions`, while Copilot uses `--autopilot --allow-all --no-ask-user` (no dashboard `permissionMode` control)
|
|
410
410
|
- **Output format** — `stream-json` (real-time streaming for live dashboard + completion recovery)
|
|
411
411
|
|
|
412
412
|
### Post-Completion
|
|
@@ -54,7 +54,6 @@ function renderAgents(agents) {
|
|
|
54
54
|
})()}
|
|
55
55
|
${a._blockingToolCall ? `<div style="margin-top:4px;padding:4px 8px;background:rgba(130,160,210,0.13);border:1px solid rgba(130,160,210,0.3);border-radius:4px;font-size:10px;color:var(--muted)">⏳ Blocking tool call (${escapeHtml(a._blockingToolCall.tool)}) — silent ${Math.round(a._blockingToolCall.silentMs/60000)}min, timeout in ${Math.round(a._blockingToolCall.remainingMs/60000)}min</div>` : ''}
|
|
56
56
|
${a._warning ? `<div style="margin-top:4px;padding:4px 8px;background:rgba(210,153,34,0.15);border:1px solid rgba(210,153,34,0.3);border-radius:4px;font-size:10px;color:var(--yellow)">⚠ ${escapeHtml(a._warning)}</div>` : ''}
|
|
57
|
-
${a._permissionMode && a._permissionMode !== 'bypassPermissions' && !a._warning ? `<div style="margin-top:4px;font-size:9px;color:var(--muted)">Permission mode: ${escapeHtml(a._permissionMode)}</div>` : ''}
|
|
58
57
|
${a.resultSummary ? `<div class="agent-result" title="${escapeHtml(a.resultSummary)}">${renderMd(a.resultSummary.slice(0, 200))}${a.resultSummary.length > 200 ? '...' : ''}</div>` : ''}
|
|
59
58
|
</div>
|
|
60
59
|
`).join('');
|
package/dashboard/js/settings.js
CHANGED
|
@@ -258,20 +258,12 @@ async function openSettings() {
|
|
|
258
258
|
'</div>' +
|
|
259
259
|
|
|
260
260
|
'<h3 style="font-size:13px;color:var(--blue);margin-bottom:8px">Claude CLI</h3>' +
|
|
261
|
-
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:
|
|
261
|
+
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px">' +
|
|
262
262
|
settingsField('Output Format', 'set-outputFormat', c.outputFormat || 'stream-json', '', '') +
|
|
263
|
-
settingsField('Allowed Tools', 'set-allowedTools', c.allowedTools || '', '', '
|
|
263
|
+
settingsField('Allowed Tools', 'set-allowedTools', c.allowedTools || '', '', 'Claude allow-list passed through for compatibility; runtime bypass flags are adapter-owned.') +
|
|
264
264
|
'</div>' +
|
|
265
|
-
'<div style="margin-bottom:16px">' +
|
|
266
|
-
'
|
|
267
|
-
'<select id="set-permissionMode" style="width:100%;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:12px">' +
|
|
268
|
-
'<option value="bypassPermissions"' + ((c.permissionMode || 'bypassPermissions') === 'bypassPermissions' ? ' selected' : '') + '>Bypass (recommended) — agents run autonomously without permission prompts</option>' +
|
|
269
|
-
'<option value="auto"' + ((c.permissionMode) === 'auto' ? ' selected' : '') + '>Auto — auto-approve safe tools, prompt for risky ones (agents may hang on risky tools)</option>' +
|
|
270
|
-
'<option value="default"' + ((c.permissionMode) === 'default' ? ' selected' : '') + '>Default — prompt for every tool (agents WILL hang — not recommended)</option>' +
|
|
271
|
-
'</select>' +
|
|
272
|
-
'<div id="set-permissionMode-warn" style="font-size:9px;margin-top:4px;padding:4px 8px;border-radius:4px;' + ((c.permissionMode && c.permissionMode !== 'bypassPermissions') ? 'display:block;background:rgba(248,81,73,0.1);color:var(--red)' : 'display:none') + '">' +
|
|
273
|
-
'\u26A0 Tools listed in Allowed Tools above are auto-approved even in non-bypass modes. Agents will only hang if they try to use a tool NOT on that list (e.g. MCP tools). In bypass mode, all tools are approved automatically.' +
|
|
274
|
-
'</div>' +
|
|
265
|
+
'<div style="font-size:10px;color:var(--muted);margin-bottom:16px;padding:6px 8px;border:1px solid var(--border);border-radius:4px;background:var(--surface-subtle, rgba(130,160,210,0.08))">' +
|
|
266
|
+
'Permission bypass is runtime-owned: Claude agents use <code>--dangerously-skip-permissions</code>; Copilot agents use <code>--autopilot --allow-all --no-ask-user</code>. There is no dashboard permission-mode setting.' +
|
|
275
267
|
'</div>' +
|
|
276
268
|
|
|
277
269
|
'<h3 style="font-size:13px;color:var(--blue);margin-bottom:8px">Agents</h3>' +
|
|
@@ -314,20 +306,6 @@ async function openSettings() {
|
|
|
314
306
|
document.getElementById('modal-body').style.whiteSpace = '';
|
|
315
307
|
document.getElementById('modal').classList.add('open');
|
|
316
308
|
|
|
317
|
-
// Wire permission mode warning toggle
|
|
318
|
-
var pmSelect = document.getElementById('set-permissionMode');
|
|
319
|
-
if (pmSelect) pmSelect.addEventListener('change', function() {
|
|
320
|
-
var warn = document.getElementById('set-permissionMode-warn');
|
|
321
|
-
if (!warn) return;
|
|
322
|
-
if (this.value !== 'bypassPermissions') {
|
|
323
|
-
warn.style.display = 'block';
|
|
324
|
-
warn.style.background = 'rgba(248,81,73,0.1)';
|
|
325
|
-
warn.style.color = 'var(--red)';
|
|
326
|
-
} else {
|
|
327
|
-
warn.style.display = 'none';
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
|
|
331
309
|
// ── Runtime fleet wiring (P-7a5c1f8e) ──────────────────────────────────────
|
|
332
310
|
// 1. Load registered runtimes into the defaultCli + ccCli dropdowns.
|
|
333
311
|
// 2. Load models for the selected defaultCli into the defaultModel input.
|
|
@@ -595,7 +573,6 @@ async function saveSettings() {
|
|
|
595
573
|
const claudePayload = {
|
|
596
574
|
outputFormat: document.getElementById('set-outputFormat').value,
|
|
597
575
|
allowedTools: document.getElementById('set-allowedTools').value,
|
|
598
|
-
permissionMode: document.getElementById('set-permissionMode').value,
|
|
599
576
|
};
|
|
600
577
|
|
|
601
578
|
const teamsPayload = {
|
package/dashboard.js
CHANGED
|
@@ -5405,6 +5405,12 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5405
5405
|
} catch (e) { return jsonReply(res, e.statusCode || 500, { error: e.message }); }
|
|
5406
5406
|
}
|
|
5407
5407
|
|
|
5408
|
+
function settingsClaudeConfig(config) {
|
|
5409
|
+
const claude = { ...shared.DEFAULT_CLAUDE, ...(config.claude || {}) };
|
|
5410
|
+
delete claude.permissionMode;
|
|
5411
|
+
return claude;
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5408
5414
|
async function handleSettingsRead(req, res) {
|
|
5409
5415
|
try {
|
|
5410
5416
|
const config = queries.getConfig();
|
|
@@ -5414,7 +5420,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5414
5420
|
if (engine.prPollCommentsEvery === undefined && engine.adoPollCommentsEvery !== undefined) engine.prPollCommentsEvery = engine.adoPollCommentsEvery;
|
|
5415
5421
|
return jsonReply(res, 200, {
|
|
5416
5422
|
engine,
|
|
5417
|
-
claude:
|
|
5423
|
+
claude: settingsClaudeConfig(config),
|
|
5418
5424
|
agents: config.agents || {},
|
|
5419
5425
|
teams: { ...shared.ENGINE_DEFAULTS.teams, ...(config.teams || {}) },
|
|
5420
5426
|
projects: (config.projects || []).map(p => ({
|
|
@@ -5437,6 +5443,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5437
5443
|
if (!config.engine) config.engine = {};
|
|
5438
5444
|
if (!config.claude) config.claude = {};
|
|
5439
5445
|
if (!config.agents) config.agents = {};
|
|
5446
|
+
delete config.claude.permissionMode;
|
|
5440
5447
|
|
|
5441
5448
|
const _clamped = [];
|
|
5442
5449
|
const _engineModelDiscovery = require('./engine/model-discovery');
|
|
@@ -5607,10 +5614,6 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5607
5614
|
for (const key of ['allowedTools', 'outputFormat']) {
|
|
5608
5615
|
if (body.claude[key] !== undefined) config.claude[key] = String(body.claude[key]);
|
|
5609
5616
|
}
|
|
5610
|
-
if (body.claude.permissionMode !== undefined) {
|
|
5611
|
-
const valid = ['bypassPermissions', 'auto', 'default'];
|
|
5612
|
-
config.claude.permissionMode = valid.includes(body.claude.permissionMode) ? body.claude.permissionMode : 'bypassPermissions';
|
|
5613
|
-
}
|
|
5614
5617
|
}
|
|
5615
5618
|
|
|
5616
5619
|
if (body.agents) {
|
package/docs/auto-discovery.md
CHANGED
|
@@ -250,10 +250,12 @@ Combines:
|
|
|
250
250
|
```bash
|
|
251
251
|
claude -p --system-prompt-file <sysprompt-file> \
|
|
252
252
|
--output-format stream-json --max-turns 100 --verbose \
|
|
253
|
-
--
|
|
253
|
+
--dangerously-skip-permissions
|
|
254
254
|
# Prompt text is piped via stdin (not passed as an arg).
|
|
255
255
|
# Agent dispatches route through engine/spawn-agent.js; CC / doc-chat use a direct
|
|
256
256
|
# spawn path in engine/llm.js that bypasses spawn-agent.js entirely.
|
|
257
|
+
# Permission bypass is runtime-owned: Copilot uses
|
|
258
|
+
# --autopilot --allow-all --no-ask-user instead.
|
|
257
259
|
```
|
|
258
260
|
|
|
259
261
|
- Process runs in the worktree directory (or rootDir for reviews)
|
|
@@ -63,9 +63,9 @@ proc.stdin.end();
|
|
|
63
63
|
|
|
64
64
|
Fix: switched to `--output-format stream-json` — streams events as they happen.
|
|
65
65
|
|
|
66
|
-
### Permission
|
|
66
|
+
### Permission Bypass
|
|
67
67
|
|
|
68
|
-
Agents would hang waiting for permission prompts (invisible in headless mode).
|
|
68
|
+
Agents would hang waiting for permission prompts (invisible in headless mode). Modern Minions keeps bypass behavior inside runtime adapters: Claude emits `--dangerously-skip-permissions`; Copilot emits `--autopilot --allow-all --no-ask-user`. The legacy `config.claude.permissionMode` key is ignored.
|
|
69
69
|
|
|
70
70
|
### CLAUDECODE Environment Variable
|
|
71
71
|
|
|
@@ -85,7 +85,7 @@ When the engine restarts, the in-memory `activeProcesses` Map is lost. Active di
|
|
|
85
85
|
|
|
86
86
|
**Dispatch 1773292681199** — Dallas, central work item, auto-route:
|
|
87
87
|
|
|
88
|
-
1. Engine spawns `node spawn-agent.js prompt.md sysprompt.md --output-format stream-json --verbose
|
|
88
|
+
1. Engine spawns `node spawn-agent.js prompt.md sysprompt.md --runtime claude --output-format stream-json --verbose`
|
|
89
89
|
2. spawn-agent.js resolves `cli.js`, spawns `node cli.js -p --system-prompt <content> ...`
|
|
90
90
|
3. Prompt piped via stdin — no shell interpretation
|
|
91
91
|
4. MCP servers connect (azure-ado, azure-kusto, mobile, DevBox)
|
package/docs/deprecated.json
CHANGED
|
@@ -22,5 +22,13 @@
|
|
|
22
22
|
"reason": "These cadence settings gate both ADO and GitHub PR polling, so the ADO-prefixed names are misleading.",
|
|
23
23
|
"locations": ["engine.js read-side fallback from config.engine.adoPollStatusEvery/adoPollCommentsEvery", "dashboard.js handleSettingsRead/handleSettingsUpdate alias fallback for old keys"],
|
|
24
24
|
"cleanup": "Remove the adoPoll* alias fallback reads after existing configs have been migrated to prPollStatusEvery/prPollCommentsEvery."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "config-claude-permission-mode",
|
|
28
|
+
"summary": "config.claude.permissionMode ignored; runtime adapters own permission bypass flags",
|
|
29
|
+
"deprecated": "2026-05-04",
|
|
30
|
+
"reason": "Claude and Copilot require different non-interactive bypass flags, so a shared Claude config field was misleading and no longer controls spawns.",
|
|
31
|
+
"locations": ["dashboard.js settings update strips config.claude.permissionMode", "dashboard/js/settings.js no longer renders a Permission Mode selector"],
|
|
32
|
+
"cleanup": "After old configs have been rewritten through settings, remove the deprecated-field preflight warning entry for permissionMode."
|
|
25
33
|
}
|
|
26
34
|
]
|
package/engine/github.js
CHANGED
|
@@ -39,6 +39,19 @@ function getRepoSlug(project) {
|
|
|
39
39
|
return `${org}/${repo}`;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function _hasMinionsReviewVerdict(body) {
|
|
43
|
+
return /(?:^|\n)\s*\*{0,2}VERDICT[:\s]+\*{0,2}(?:APPROVE|REQUEST[_\s-]?CHANGES)\*{0,2}\b/i.test(String(body || ''));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function _isAgentComment(c) {
|
|
47
|
+
const body = c.body || '';
|
|
48
|
+
if (_hasMinionsReviewVerdict(body)) return true;
|
|
49
|
+
if (/\bMinions\s*\(/i.test(body)) return true;
|
|
50
|
+
if (/\bby\s+Minions\b/i.test(body)) return true;
|
|
51
|
+
if (/\[minions\]/i.test(body)) return true;
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
42
55
|
// ─── Per-Repo Poll Backoff ──────────────────────────────────────────────────
|
|
43
56
|
// Tracks consecutive poll failures per repo slug to avoid spamming logs when
|
|
44
57
|
// a repo is inaccessible. Backoff doubles each failure: 2min, 4min, 8min, 16min, max 30min.
|
|
@@ -572,13 +585,6 @@ async function pollPrHumanComments(config) {
|
|
|
572
585
|
if (/!\[.*\]\(https?:\/\/.*badge/i.test(body)) return true;
|
|
573
586
|
return false;
|
|
574
587
|
}
|
|
575
|
-
function _isAgentComment(c) {
|
|
576
|
-
const body = c.body || '';
|
|
577
|
-
if (/\bMinions\s*\(/i.test(body)) return true;
|
|
578
|
-
if (/\bby\s+Minions\b/i.test(body)) return true;
|
|
579
|
-
if (/\[minions\]/i.test(body)) return true;
|
|
580
|
-
return false;
|
|
581
|
-
}
|
|
582
588
|
const actionableComments = allComments.filter(c => !_isIgnoredComment(c));
|
|
583
589
|
|
|
584
590
|
const cutoffStr = pr.humanFeedback?.lastProcessedCommentDate || pr.created || '1970-01-01';
|
|
@@ -907,4 +913,6 @@ module.exports = {
|
|
|
907
913
|
GH_MAX_BUFFER, // exported for testing
|
|
908
914
|
GH_POLL_BACKOFF_BASE_MS, // exported for testing
|
|
909
915
|
GH_POLL_BACKOFF_MAX_MS, // exported for testing
|
|
916
|
+
_hasMinionsReviewVerdict, // exported for testing
|
|
917
|
+
_isAgentComment, // exported for testing
|
|
910
918
|
};
|
package/engine/lifecycle.js
CHANGED
|
@@ -1266,11 +1266,61 @@ function isReviewBailout(text) {
|
|
|
1266
1266
|
return /bail(ing)?\s+out/i.test(text) || /already\s+posted/i.test(text);
|
|
1267
1267
|
}
|
|
1268
1268
|
|
|
1269
|
-
|
|
1269
|
+
function reviewPrRefFromCompletion(completion) {
|
|
1270
|
+
if (!completion || typeof completion !== 'object') return null;
|
|
1271
|
+
const value = String(completion.pr || completion.pull_request || completion.pullRequest || '').trim();
|
|
1272
|
+
if (!value || /^N\/?A$/i.test(value)) return null;
|
|
1273
|
+
return value;
|
|
1274
|
+
}
|
|
1270
1275
|
|
|
1271
|
-
|
|
1276
|
+
function centralPrPath() {
|
|
1277
|
+
return path.join(path.resolve(MINIONS_DIR, '..'), '.minions', 'pull-requests.json');
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
function resolveReviewPrContext(pr, project, config, structuredCompletion = null) {
|
|
1281
|
+
const refs = [pr, reviewPrRefFromCompletion(structuredCompletion)].filter(Boolean);
|
|
1282
|
+
if (refs.length === 0) return null;
|
|
1283
|
+
|
|
1284
|
+
const projects = shared.getProjects(config);
|
|
1285
|
+
const projectCandidates = [];
|
|
1286
|
+
if (project) projectCandidates.push(project);
|
|
1287
|
+
for (const p of projects) {
|
|
1288
|
+
if (!projectCandidates.some(existing => existing?.name === p.name)) projectCandidates.push(p);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
for (const candidateProject of projectCandidates) {
|
|
1292
|
+
const prPath = shared.projectPrPath(candidateProject);
|
|
1293
|
+
const prs = safeJson(prPath) || [];
|
|
1294
|
+
for (const ref of refs) {
|
|
1295
|
+
const target = shared.findPrRecord(prs, ref, candidateProject);
|
|
1296
|
+
if (target) return { pr: { ...target }, project: candidateProject, prPath };
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
const centralPath = centralPrPath();
|
|
1301
|
+
const centralPrs = safeJson(centralPath) || [];
|
|
1302
|
+
for (const ref of refs) {
|
|
1303
|
+
const target = shared.findPrRecord(centralPrs, ref, null);
|
|
1304
|
+
if (target) return { pr: { ...target }, project: null, prPath: centralPath };
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
return pr?.id
|
|
1308
|
+
? { pr, project: project || null, prPath: project ? shared.projectPrPath(project) : centralPath }
|
|
1309
|
+
: null;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
async function updatePrAfterReview(agentId, pr, project, config, resultSummary, structuredCompletion = null) {
|
|
1272
1313
|
|
|
1273
1314
|
if (!config) config = getConfig();
|
|
1315
|
+
const reviewContext = resolveReviewPrContext(pr, project, config, structuredCompletion);
|
|
1316
|
+
if (!reviewContext?.pr?.id) {
|
|
1317
|
+
const reportedPr = reviewPrRefFromCompletion(structuredCompletion);
|
|
1318
|
+
if (reportedPr) log('warn', `Review completion reported PR ${reportedPr}, but no tracked PR record was found`);
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
const reviewPr = reviewContext.pr;
|
|
1322
|
+
const reviewProject = reviewContext.project;
|
|
1323
|
+
const prPath = reviewContext.prPath;
|
|
1274
1324
|
const reviewerName = config.agents?.[agentId]?.name || agentId;
|
|
1275
1325
|
const dispatch = getDispatch();
|
|
1276
1326
|
const completedEntry = (dispatch.completed || []).find(d => d.agent === agentId && d.type === 'review');
|
|
@@ -1280,31 +1330,30 @@ async function updatePrAfterReview(agentId, pr, project, config, resultSummary,
|
|
|
1280
1330
|
// The poller will pick up the real status on the next cycle (~3 min).
|
|
1281
1331
|
let postReviewStatus = null; // null = don't change
|
|
1282
1332
|
try {
|
|
1283
|
-
const projectObj =
|
|
1333
|
+
const projectObj = reviewProject || shared.getProjects(config)[0];
|
|
1284
1334
|
if (projectObj) {
|
|
1285
1335
|
const host = projectObj.repoHost || 'ado';
|
|
1286
1336
|
const checkFn = host === 'github'
|
|
1287
1337
|
? require('./github').checkLiveReviewStatus
|
|
1288
1338
|
: require('./ado').checkLiveReviewStatus;
|
|
1289
|
-
const liveStatus = await checkFn(
|
|
1339
|
+
const liveStatus = await checkFn(reviewPr, projectObj);
|
|
1290
1340
|
if (liveStatus && liveStatus !== 'pending') postReviewStatus = liveStatus;
|
|
1291
1341
|
}
|
|
1292
|
-
} catch (e) { log('warn', `Post-review status check for ${
|
|
1342
|
+
} catch (e) { log('warn', `Post-review status check for ${reviewPr.id}: ${e.message}`); }
|
|
1293
1343
|
|
|
1294
1344
|
// Fallback: if live check returned pending (e.g., GitHub self-approval blocked), use the agent's completion report.
|
|
1295
1345
|
if (!postReviewStatus) {
|
|
1296
1346
|
const verdict = reviewVerdictFromCompletion(structuredCompletion) || parseReviewVerdict(resultSummary);
|
|
1297
1347
|
if (verdict) {
|
|
1298
1348
|
postReviewStatus = verdict;
|
|
1299
|
-
log('info', `Read review verdict from agent completion for ${
|
|
1349
|
+
log('info', `Read review verdict from agent completion for ${reviewPr.id}: ${verdict}`);
|
|
1300
1350
|
}
|
|
1301
1351
|
}
|
|
1302
1352
|
|
|
1303
|
-
const prPath = project ? shared.projectPrPath(project) : path.join(path.resolve(MINIONS_DIR, '..'), '.minions', 'pull-requests.json');
|
|
1304
1353
|
let updatedTarget = null;
|
|
1305
1354
|
shared.mutateJsonFileLocked(prPath, (prs) => {
|
|
1306
1355
|
if (!Array.isArray(prs)) return prs;
|
|
1307
|
-
const target = shared.findPrRecord(prs,
|
|
1356
|
+
const target = shared.findPrRecord(prs, reviewPr, reviewProject);
|
|
1308
1357
|
if (!target) return prs;
|
|
1309
1358
|
// Once approved, stays approved — only changes-requested can override
|
|
1310
1359
|
if (postReviewStatus) {
|
|
@@ -1323,12 +1372,12 @@ async function updatePrAfterReview(agentId, pr, project, config, resultSummary,
|
|
|
1323
1372
|
// Drop it when reviewer requests changes again — that starts a new fix cycle.
|
|
1324
1373
|
...(target.minionsReview?.fixedAt && postReviewStatus !== 'changes-requested' ? { fixedAt: target.minionsReview.fixedAt } : {}),
|
|
1325
1374
|
};
|
|
1326
|
-
updatedTarget = { ...
|
|
1375
|
+
updatedTarget = { ...reviewPr, ...target };
|
|
1327
1376
|
return prs;
|
|
1328
1377
|
}, { defaultValue: [] });
|
|
1329
1378
|
|
|
1330
1379
|
// Track reviewer for metrics purposes (separate file, separate lock)
|
|
1331
|
-
const authorAgentId = (
|
|
1380
|
+
const authorAgentId = (reviewPr.agent || '').toLowerCase();
|
|
1332
1381
|
if (authorAgentId && config.agents?.[authorAgentId]) {
|
|
1333
1382
|
shared.mutateJsonFileLocked(path.join(ENGINE_DIR, 'metrics.json'), (metrics) => {
|
|
1334
1383
|
if (!metrics[authorAgentId]) metrics[authorAgentId] = { ...DEFAULT_AGENT_METRICS };
|
|
@@ -1338,7 +1387,7 @@ async function updatePrAfterReview(agentId, pr, project, config, resultSummary,
|
|
|
1338
1387
|
}, { defaultValue: {} });
|
|
1339
1388
|
}
|
|
1340
1389
|
|
|
1341
|
-
log('info', `Updated ${
|
|
1390
|
+
log('info', `Updated ${reviewPr.id} → minions review: ${postReviewStatus || 'waiting'} by ${reviewerName}`);
|
|
1342
1391
|
if (updatedTarget) createReviewFeedbackForAuthor(agentId, updatedTarget, config);
|
|
1343
1392
|
}
|
|
1344
1393
|
|
package/engine/queries.js
CHANGED
|
@@ -428,25 +428,11 @@ function getAgentStatus(agentId) {
|
|
|
428
428
|
if (active._blockingToolCall) {
|
|
429
429
|
result._blockingToolCall = active._blockingToolCall;
|
|
430
430
|
}
|
|
431
|
-
// Detect
|
|
431
|
+
// Detect in-flight tools: read only head+tail of live-output.log (max 2KB total)
|
|
432
432
|
try {
|
|
433
433
|
const liveLogPath = path.join(AGENTS_DIR, agentId, 'live-output.log');
|
|
434
434
|
const { head, tail } = readHeadTail(liveLogPath, 1024);
|
|
435
435
|
if (head) {
|
|
436
|
-
// Check init message (in head) for permission mode
|
|
437
|
-
const initMatch = head.match(/"permissionMode"\s*:\s*"([^"]+)"/);
|
|
438
|
-
if (initMatch && initMatch[1] !== 'bypassPermissions') {
|
|
439
|
-
result._permissionMode = initMatch[1];
|
|
440
|
-
}
|
|
441
|
-
// Check if agent has been silent for >60s (use tail for recent activity)
|
|
442
|
-
const lastLine = tail.trimEnd().split('\n').pop();
|
|
443
|
-
if (lastLine && lastLine.includes('"type":"assistant"') && lastLine.includes('"tool_use"')) {
|
|
444
|
-
const liveStat = fs.statSync(liveLogPath);
|
|
445
|
-
const silentMs = Date.now() - liveStat.mtimeMs;
|
|
446
|
-
if (silentMs > 60000 && result._permissionMode) {
|
|
447
|
-
result._warning = 'Possibly waiting for permission approval — agent is not in bypass mode';
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
436
|
// Detect in-flight tool calls (task_started with no task_notification)
|
|
451
437
|
const inFlight = detectInFlightTool(tail);
|
|
452
438
|
if (inFlight && inFlight.description) {
|
|
@@ -570,7 +556,6 @@ function getAgents(config) {
|
|
|
570
556
|
completed_at: s.completed_at || null,
|
|
571
557
|
_blockingToolCall: s._blockingToolCall || null,
|
|
572
558
|
_warning: s._warning || null,
|
|
573
|
-
_permissionMode: s._permissionMode || null,
|
|
574
559
|
chartered, inboxCount: inboxFiles.length + steeringInboxFiles.length
|
|
575
560
|
};
|
|
576
561
|
});
|
package/engine/shared.js
CHANGED
|
@@ -851,7 +851,7 @@ const ENGINE_DEFAULTS = {
|
|
|
851
851
|
// Backward-compat: keep `engine.claude.*` field family deprecation tracker. Listed here so preflight
|
|
852
852
|
// knows which subkeys to flag as deprecated. Do not consume `claude.*` in new code — use the runtime
|
|
853
853
|
// adapter system (engine/runtimes/) and the resolveAgent*/resolveCc* helpers instead.
|
|
854
|
-
_deprecatedConfigClaudeFields: ['binary', 'outputFormat', 'allowedTools', 'maxTurns', 'effort', 'budgetCap'],
|
|
854
|
+
_deprecatedConfigClaudeFields: ['binary', 'outputFormat', 'allowedTools', 'permissionMode', 'maxTurns', 'effort', 'budgetCap'],
|
|
855
855
|
// Teams integration — config.teams shape: { enabled, appId, appPassword, certPath, privateKeyPath, tenantId, notifyEvents, ccMirror, inboxPollInterval }
|
|
856
856
|
// Auth modes: (1) appId + appPassword (client secret), or (2) appId + certPath + privateKeyPath + tenantId (certificate)
|
|
857
857
|
teams: {
|
|
@@ -1310,14 +1310,27 @@ function projectPrPath(project) {
|
|
|
1310
1310
|
return path.join(projectStateDir(project), 'pull-requests.json');
|
|
1311
1311
|
}
|
|
1312
1312
|
|
|
1313
|
+
function comparablePath(filePath) {
|
|
1314
|
+
const resolved = path.resolve(filePath);
|
|
1315
|
+
try {
|
|
1316
|
+
return fs.realpathSync.native(resolved);
|
|
1317
|
+
} catch {
|
|
1318
|
+
try {
|
|
1319
|
+
return path.join(fs.realpathSync.native(path.dirname(resolved)), path.basename(resolved));
|
|
1320
|
+
} catch {
|
|
1321
|
+
return resolved;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1313
1326
|
function resolveProjectForPrPath(filePath, config = null) {
|
|
1314
|
-
const resolvedPaths = new Set([
|
|
1327
|
+
const resolvedPaths = new Set([comparablePath(filePath)]);
|
|
1315
1328
|
if (filePath && !path.isAbsolute(filePath)) {
|
|
1316
|
-
resolvedPaths.add(path.resolve(MINIONS_DIR, filePath));
|
|
1329
|
+
resolvedPaths.add(comparablePath(path.resolve(MINIONS_DIR, filePath)));
|
|
1317
1330
|
}
|
|
1318
1331
|
const projects = getProjects(config);
|
|
1319
1332
|
for (const project of projects) {
|
|
1320
|
-
if (resolvedPaths.has(
|
|
1333
|
+
if (resolvedPaths.has(comparablePath(projectPrPath(project)))) return project;
|
|
1321
1334
|
}
|
|
1322
1335
|
if (projects.length === 1) return projects[0];
|
|
1323
1336
|
return null;
|
package/minions.js
CHANGED
|
@@ -470,6 +470,7 @@ async function initMinions({ skipScan = false, scanRoot, scanDepth } = {}) {
|
|
|
470
470
|
if (config.engine[k] === undefined) config.engine[k] = v;
|
|
471
471
|
}
|
|
472
472
|
if (!config.claude) config.claude = {};
|
|
473
|
+
delete config.claude.permissionMode;
|
|
473
474
|
for (const [k, v] of Object.entries(DEFAULT_CLAUDE)) {
|
|
474
475
|
if (config.claude[k] === undefined) config.claude[k] = v;
|
|
475
476
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1694",
|
|
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"
|