@yemi33/minions 0.1.799 → 0.1.801
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 +3 -1
- package/dashboard/js/settings.js +2 -0
- package/dashboard.js +35 -17
- package/engine/ado.js +3 -1
- package/engine/github.js +2 -0
- package/engine/shared.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.801 (2026-04-10)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
|
+
- configurable ignored comment authors — auto-filtered, never trigger fixes
|
|
6
7
|
- add open-source community sources to agentic research pipeline
|
|
7
8
|
- broaden daily pipeline research from Anthropic-only to full agentic space
|
|
8
9
|
- cap review→fix cycles at evalMaxIterations (default 3)
|
|
9
10
|
|
|
10
11
|
### Fixes
|
|
12
|
+
- move ignoredAuthors construction outside inner comment loop (ADO)
|
|
11
13
|
- approved is permanent — no path can ever downgrade it
|
|
12
14
|
- allow fix dispatch on approved PRs (only guard is in updatePrAfterFix)
|
|
13
15
|
- bot comment on approved PR can no longer reset vote
|
package/dashboard/js/settings.js
CHANGED
|
@@ -54,6 +54,7 @@ async function openSettings() {
|
|
|
54
54
|
settingsField('Eval Max Cost', 'set-evalMaxCost', e.evalMaxCost === null || e.evalMaxCost === undefined ? '' : e.evalMaxCost, '$', 'USD ceiling per work item across all eval iterations (blank = no limit)') +
|
|
55
55
|
settingsField('Max Build Fix Attempts', 'set-maxBuildFixAttempts', e.maxBuildFixAttempts || 3, '', 'Max auto-fix dispatches per PR before escalating to human (1-10)') +
|
|
56
56
|
settingsField('Version Check Interval', 'set-versionCheckInterval', e.versionCheckInterval || 3600000, 'ms', 'How often to check npm for updates (default: 1 hour)') +
|
|
57
|
+
settingsField('Ignored Comment Authors', 'set-ignoredCommentAuthors', (e.ignoredCommentAuthors || []).join(', '), '', 'Comma-separated usernames — comments auto-closed, never trigger fixes') +
|
|
57
58
|
'</div>' +
|
|
58
59
|
|
|
59
60
|
'<h3 style="font-size:13px;color:var(--blue);margin-bottom:8px">Max Turns by Task Type</h3>' +
|
|
@@ -210,6 +211,7 @@ async function saveSettings() {
|
|
|
210
211
|
evalMaxIterations: document.getElementById('set-evalMaxIterations').value,
|
|
211
212
|
evalMaxCost: document.getElementById('set-evalMaxCost').value || null,
|
|
212
213
|
maxBuildFixAttempts: document.getElementById('set-maxBuildFixAttempts').value,
|
|
214
|
+
ignoredCommentAuthors: document.getElementById('set-ignoredCommentAuthors').value,
|
|
213
215
|
versionCheckInterval: document.getElementById('set-versionCheckInterval').value,
|
|
214
216
|
ccModel: document.getElementById('set-ccModel').value,
|
|
215
217
|
ccEffort: document.getElementById('set-ccEffort').value || null,
|
package/dashboard.js
CHANGED
|
@@ -461,7 +461,8 @@ setInterval(() => {
|
|
|
461
461
|
|
|
462
462
|
// ── Command Center: session state + helpers ─────────────────────────────────
|
|
463
463
|
|
|
464
|
-
|
|
464
|
+
// Sessions never expire by time — user controls lifecycle via tabs (new tab = new session, /clear = reset).
|
|
465
|
+
// Only invalidated by: system prompt change, explicit clear, or tab close.
|
|
465
466
|
const CC_SESSION_MAX_TURNS = Infinity;
|
|
466
467
|
let ccSession = { sessionId: null, createdAt: null, lastActiveAt: null, turnCount: 0 };
|
|
467
468
|
const ccInFlightTabs = new Set(); // per-tab in-flight tracking for parallel CC requests
|
|
@@ -477,17 +478,13 @@ function ccSessionValid() {
|
|
|
477
478
|
ccSession = { sessionId: null, createdAt: null, lastActiveAt: null, turnCount: 0 };
|
|
478
479
|
return false;
|
|
479
480
|
}
|
|
480
|
-
|
|
481
|
-
return age < CC_SESSION_EXPIRY_MS && ccSession.turnCount < CC_SESSION_MAX_TURNS;
|
|
481
|
+
return ccSession.turnCount < CC_SESSION_MAX_TURNS;
|
|
482
482
|
}
|
|
483
483
|
|
|
484
484
|
// Load persisted CC session on startup
|
|
485
485
|
try {
|
|
486
486
|
const saved = safeJson(path.join(ENGINE_DIR, 'cc-session.json'));
|
|
487
|
-
if (saved && saved.sessionId)
|
|
488
|
-
const age = Date.now() - new Date(saved.lastActiveAt || 0).getTime();
|
|
489
|
-
if (age < CC_SESSION_EXPIRY_MS) ccSession = saved;
|
|
490
|
-
}
|
|
487
|
+
if (saved && saved.sessionId) ccSession = saved;
|
|
491
488
|
} catch { /* optional */ }
|
|
492
489
|
|
|
493
490
|
// Static system prompt — baked into session on creation, never changes
|
|
@@ -584,10 +581,8 @@ const docSessions = new Map(); // key → { sessionId, lastActiveAt, turnCount }
|
|
|
584
581
|
try {
|
|
585
582
|
const saved = safeJson(DOC_SESSIONS_PATH);
|
|
586
583
|
if (saved && typeof saved === 'object') {
|
|
587
|
-
const now = Date.now();
|
|
588
584
|
for (const [key, s] of Object.entries(saved)) {
|
|
589
|
-
|
|
590
|
-
if (age < CC_SESSION_EXPIRY_MS && s.turnCount < CC_SESSION_MAX_TURNS) {
|
|
585
|
+
if (s.turnCount < CC_SESSION_MAX_TURNS) {
|
|
591
586
|
docSessions.set(key, s);
|
|
592
587
|
}
|
|
593
588
|
}
|
|
@@ -608,8 +603,7 @@ function resolveSession(store, key) {
|
|
|
608
603
|
if (!key) return null;
|
|
609
604
|
const s = docSessions.get(key);
|
|
610
605
|
if (!s) return null;
|
|
611
|
-
|
|
612
|
-
if (age > CC_SESSION_EXPIRY_MS || s.turnCount >= CC_SESSION_MAX_TURNS) {
|
|
606
|
+
if (s.turnCount >= CC_SESSION_MAX_TURNS) {
|
|
613
607
|
docSessions.delete(key);
|
|
614
608
|
persistDocSessions();
|
|
615
609
|
return null;
|
|
@@ -3342,9 +3336,15 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3342
3336
|
ccInFlightTabs.add(tabId);
|
|
3343
3337
|
|
|
3344
3338
|
try {
|
|
3339
|
+
let sessionReset = false;
|
|
3345
3340
|
if (body.sessionId && body.sessionId !== ccSession.sessionId) {
|
|
3346
3341
|
ccSession = { sessionId: null, createdAt: null, lastActiveAt: null, turnCount: 0 };
|
|
3347
3342
|
}
|
|
3343
|
+
// Detect prompt hash change — force fresh session
|
|
3344
|
+
if (body.sessionId && ccSession._promptHash && ccSession._promptHash !== _ccPromptHash) {
|
|
3345
|
+
ccSession = { sessionId: null, createdAt: null, lastActiveAt: null, turnCount: 0 };
|
|
3346
|
+
sessionReset = true;
|
|
3347
|
+
}
|
|
3348
3348
|
const wasResume = !!(body.sessionId && body.sessionId === ccSession.sessionId && ccSessionValid());
|
|
3349
3349
|
|
|
3350
3350
|
const result = await ccCall(body.message, { store: 'cc' });
|
|
@@ -3365,7 +3365,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3365
3365
|
});
|
|
3366
3366
|
}
|
|
3367
3367
|
|
|
3368
|
-
|
|
3368
|
+
const reply = { ...parseCCActions(result.text), sessionId: ccSession.sessionId, newSession: !wasResume };
|
|
3369
|
+
if (sessionReset) reply.sessionReset = true;
|
|
3370
|
+
return jsonReply(res, 200, reply);
|
|
3369
3371
|
} finally {
|
|
3370
3372
|
ccInFlightTabs.delete(tabId);
|
|
3371
3373
|
}
|
|
@@ -3397,7 +3399,17 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3397
3399
|
|
|
3398
3400
|
try {
|
|
3399
3401
|
// Session management — per-tab: use sessionId from request, don't mutate global ccSession
|
|
3400
|
-
|
|
3402
|
+
let tabSessionId = body.sessionId || null;
|
|
3403
|
+
let sessionReset = false;
|
|
3404
|
+
// If system prompt changed since this session was created, force a fresh session
|
|
3405
|
+
if (tabSessionId) {
|
|
3406
|
+
const sessions = shared.safeJsonArr(CC_SESSIONS_PATH);
|
|
3407
|
+
const tabEntry = sessions.find(s => s.id === (body.tabId || 'default'));
|
|
3408
|
+
if (tabEntry && tabEntry._promptHash && tabEntry._promptHash !== _ccPromptHash) {
|
|
3409
|
+
tabSessionId = null;
|
|
3410
|
+
sessionReset = true;
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3401
3413
|
const wasResume = !!tabSessionId;
|
|
3402
3414
|
const sessionId = tabSessionId || null;
|
|
3403
3415
|
const preamble = wasResume ? '' : buildCCStatePreamble();
|
|
@@ -3451,10 +3463,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3451
3463
|
if (existing) {
|
|
3452
3464
|
existing.sessionId = responseSessionId;
|
|
3453
3465
|
existing.lastActiveAt = new Date(now).toISOString();
|
|
3454
|
-
existing.turnCount = (existing.turnCount || 0) + 1;
|
|
3466
|
+
existing.turnCount = sessionReset ? 1 : (existing.turnCount || 0) + 1;
|
|
3455
3467
|
existing.preview = preview;
|
|
3468
|
+
existing._promptHash = _ccPromptHash;
|
|
3456
3469
|
} else {
|
|
3457
|
-
sessions.push({ id: tabId, title: (body.message || 'New chat').slice(0, 40), sessionId: responseSessionId, createdAt: new Date(now).toISOString(), lastActiveAt: new Date(now).toISOString(), turnCount: 1, preview });
|
|
3470
|
+
sessions.push({ id: tabId, title: (body.message || 'New chat').slice(0, 40), sessionId: responseSessionId, createdAt: new Date(now).toISOString(), lastActiveAt: new Date(now).toISOString(), turnCount: 1, preview, _promptHash: _ccPromptHash });
|
|
3458
3471
|
}
|
|
3459
3472
|
safeWrite(CC_SESSIONS_PATH, sessions);
|
|
3460
3473
|
} catch { /* non-critical */ }
|
|
@@ -3462,7 +3475,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3462
3475
|
|
|
3463
3476
|
// Send final result with actions
|
|
3464
3477
|
const { text: displayText, actions } = parseCCActions(result.text);
|
|
3465
|
-
|
|
3478
|
+
const donePayload = { type: 'done', text: displayText, actions, sessionId: responseSessionId, newSession: !wasResume };
|
|
3479
|
+
if (sessionReset) donePayload.sessionReset = true;
|
|
3480
|
+
res.write('data: ' + JSON.stringify(donePayload) + '\n\n');
|
|
3466
3481
|
_ccStreamEnded = true; res.end();
|
|
3467
3482
|
} finally {
|
|
3468
3483
|
ccInFlightTabs.delete(tabId);
|
|
@@ -3651,6 +3666,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3651
3666
|
// Eval loop settings
|
|
3652
3667
|
if (e.evalMaxIterations !== undefined) config.engine.evalMaxIterations = Math.max(1, Math.min(10, Number(e.evalMaxIterations) || D.evalMaxIterations));
|
|
3653
3668
|
if (e.evalMaxCost !== undefined) config.engine.evalMaxCost = e.evalMaxCost === null || e.evalMaxCost === '' ? null : Math.max(0, Number(e.evalMaxCost) || 0);
|
|
3669
|
+
if (e.ignoredCommentAuthors !== undefined) {
|
|
3670
|
+
config.engine.ignoredCommentAuthors = String(e.ignoredCommentAuthors || '').split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
|
|
3671
|
+
}
|
|
3654
3672
|
}
|
|
3655
3673
|
|
|
3656
3674
|
if (body.claude) {
|
package/engine/ado.js
CHANGED
|
@@ -496,13 +496,15 @@ async function pollPrHumanComments(config) {
|
|
|
496
496
|
// Collect ALL human comments on the PR for full context
|
|
497
497
|
const allHumanComments = [];
|
|
498
498
|
const newHumanComments = [];
|
|
499
|
+
const ignoredAuthors = (config.engine?.ignoredCommentAuthors || []).map(a => a.toLowerCase());
|
|
499
500
|
|
|
500
501
|
for (const thread of threads) {
|
|
501
502
|
for (const comment of (thread.comments || [])) {
|
|
502
503
|
if (!comment.content || comment.commentType === 'system') continue;
|
|
503
504
|
const content = comment.content;
|
|
504
|
-
// Skip bots and
|
|
505
|
+
// Skip bots, CI noise, and ignored authors
|
|
505
506
|
const authorName = (comment.author?.displayName || '').toLowerCase();
|
|
507
|
+
if (ignoredAuthors.some(a => authorName.includes(a))) continue;
|
|
506
508
|
if (/\b(bot|service|build|pipeline|codecov|sonar)\b/i.test(authorName)) continue;
|
|
507
509
|
if (/^#{1,3}\s*(Coverage|Build|Test|Deploy|Pipeline)\s*(Report|Status|Result|Summary)/i.test(content)) continue;
|
|
508
510
|
// Detect agent comments (included in context, but don't trigger fix)
|
package/engine/github.js
CHANGED
|
@@ -477,9 +477,11 @@ async function pollPrHumanComments(config) {
|
|
|
477
477
|
|
|
478
478
|
// Separate: agent comments (included in context, don't trigger fix) vs human comments (trigger fix)
|
|
479
479
|
// All non-bot, non-CI comments go into context. Only non-agent comments trigger pendingFix.
|
|
480
|
+
const ignoredAuthors = new Set((config.engine?.ignoredCommentAuthors || []).map(a => a.toLowerCase()));
|
|
480
481
|
function _isBot(c) {
|
|
481
482
|
if (c.user?.type === 'Bot') return true;
|
|
482
483
|
const login = (c.user?.login || '').toLowerCase();
|
|
484
|
+
if (ignoredAuthors.has(login)) return true;
|
|
483
485
|
if (/\b(bot|codecov|sonar|dependabot|renovate|github-actions|azure-pipelines)\b/i.test(login)) return true;
|
|
484
486
|
const body = c.body || '';
|
|
485
487
|
if (/^#{1,3}\s*(Coverage|Build|Test|Deploy|Pipeline)\s*(Report|Status|Result|Summary)/i.test(body)) return true;
|
package/engine/shared.js
CHANGED
|
@@ -549,6 +549,7 @@ const ENGINE_DEFAULTS = {
|
|
|
549
549
|
buildFixGracePeriod: 600000, // 10min — wait for CI to run after build fix before re-dispatching
|
|
550
550
|
autoCompletePrs: false, // auto-merge PRs when builds green + review approved (opt-in)
|
|
551
551
|
prMergeMethod: 'squash', // merge method: squash, merge, rebase
|
|
552
|
+
ignoredCommentAuthors: [], // comments from these authors are auto-closed and never trigger fixes
|
|
552
553
|
ccModel: 'sonnet', // model for Command Center and doc-chat (sonnet, haiku, opus)
|
|
553
554
|
ccEffort: null, // effort level for CC/doc-chat (null, 'low', 'medium', 'high')
|
|
554
555
|
ccMaxTurns: 50, // max tool-use turns for CC/doc-chat before CLI stops
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.801",
|
|
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"
|