@yemi33/minions 0.1.1801 → 0.1.1803
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 -0
- package/dashboard.js +44 -4
- package/engine/copilot-models.json +1 -1
- package/engine.js +51 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/dashboard.js
CHANGED
|
@@ -1929,16 +1929,52 @@ function fencedUntrustedBlock(label, content) {
|
|
|
1929
1929
|
return `### ${label}\n${fence}text\n${value}\n${fence}`;
|
|
1930
1930
|
}
|
|
1931
1931
|
|
|
1932
|
+
function _messageExplicitlyRequestsMonitoring(message) {
|
|
1933
|
+
const text = String(message || '').toLowerCase();
|
|
1934
|
+
if (!text.trim()) return false;
|
|
1935
|
+
if (/\b(?:do\s+not|don't|dont|never|without|no\s+need\s+to)\s+(?:monitor(?:ing)?|watch(?:ing)?|poll(?:ing)?|check(?:ing)?|notify(?:ing)?|ping(?:ing)?|keep(?:ing)?\s+an\s+eye)\b/.test(text)) {
|
|
1936
|
+
return false;
|
|
1937
|
+
}
|
|
1938
|
+
return [
|
|
1939
|
+
/\bmonitor(?:ing)?\b/,
|
|
1940
|
+
/\bwatch(?:ing)?\b/,
|
|
1941
|
+
/\bkeep(?:ing)?\s+an\s+eye\s+on\b/,
|
|
1942
|
+
/\bcheck(?:ing)?\s+(?:it|this|that|[^.?!\n]+)\s+periodically\b/,
|
|
1943
|
+
/\bperiodically\s+check\b/,
|
|
1944
|
+
/\bcheck(?:ing)?\s+[^.?!\n]+\bevery\s+\d+\s*(?:s|sec|seconds?|m|min|minutes?|h|hr|hours?)\b/,
|
|
1945
|
+
/\bevery\s+\d+\s*(?:s|sec|seconds?|m|min|minutes?|h|hr|hours?)\b[^.?!\n]+\b(?:check|poll|monitor|watch)\b/,
|
|
1946
|
+
/\bping\s+(?:me\s+)?(?:on|when|after)\b/,
|
|
1947
|
+
/\bping\s+on\s+completion\b/,
|
|
1948
|
+
/\bnotify\s+(?:me\s+)?(?:on|when|after)\b/,
|
|
1949
|
+
/\blet\s+me\s+know\s+(?:when|once|if)\b/,
|
|
1950
|
+
/\bpoll(?:ing)?\b/,
|
|
1951
|
+
].some(pattern => pattern.test(text));
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
function _isDispatchLikeCCAction(action) {
|
|
1955
|
+
const type = String(action?.type || '').trim().toLowerCase();
|
|
1956
|
+
return type === 'dispatch' || ['fix', 'explore', 'review', 'test'].includes(type);
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
function _filterImplicitPostDispatchActions(actions, humanMessage) {
|
|
1960
|
+
if (!Array.isArray(actions) || actions.length === 0) return [];
|
|
1961
|
+
if (_messageExplicitlyRequestsMonitoring(humanMessage)) return actions;
|
|
1962
|
+
const dispatchIdx = actions.findIndex(_isDispatchLikeCCAction);
|
|
1963
|
+
if (dispatchIdx < 0) return actions;
|
|
1964
|
+
return actions.slice(0, dispatchIdx + 1);
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1932
1967
|
// ── /loop → create-watch safety net ──────────────────────────────────────────
|
|
1933
1968
|
// CC sometimes invokes the /loop skill instead of emitting a create-watch action.
|
|
1934
1969
|
// This pure function detects /loop invocation in CC response text and synthesizes
|
|
1935
1970
|
// a create-watch action as a fallback. Returns null if no conversion needed.
|
|
1936
1971
|
|
|
1937
|
-
function _detectLoopInvocation(text, actions, toolUses) {
|
|
1972
|
+
function _detectLoopInvocation(text, actions, toolUses, humanMessage) {
|
|
1938
1973
|
const observedToolUses = Array.isArray(toolUses) ? toolUses : [];
|
|
1939
1974
|
if (!text && observedToolUses.length === 0) return null;
|
|
1940
1975
|
// If a create-watch action was already emitted, no fallback needed
|
|
1941
1976
|
if (actions && actions.some(a => a.type === 'create-watch')) return null;
|
|
1977
|
+
if (humanMessage !== undefined && !_messageExplicitlyRequestsMonitoring(humanMessage)) return null;
|
|
1942
1978
|
|
|
1943
1979
|
function _extractTargetFromValue(value, keyHint) {
|
|
1944
1980
|
if (value == null) return null;
|
|
@@ -6509,12 +6545,13 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
6509
6545
|
const parsed = parseCCActions(result.text);
|
|
6510
6546
|
const toolUses = Array.isArray(result.toolUses) ? result.toolUses : _extractToolUsesFromRaw(result.raw);
|
|
6511
6547
|
// Safety net: detect /loop invocation and convert to create-watch
|
|
6512
|
-
const _loopWatch = _detectLoopInvocation(parsed.text, parsed.actions, toolUses);
|
|
6548
|
+
const _loopWatch = _detectLoopInvocation(parsed.text, parsed.actions, toolUses, body.message);
|
|
6513
6549
|
if (_loopWatch) {
|
|
6514
6550
|
parsed.actions.push(_loopWatch);
|
|
6515
6551
|
console.warn('[CC] /loop invocation detected — converted to create-watch');
|
|
6516
6552
|
try { shared.log('warn', '/loop invocation detected in CC response — auto-converted to create-watch'); } catch {}
|
|
6517
6553
|
}
|
|
6554
|
+
parsed.actions = _filterImplicitPostDispatchActions(parsed.actions, body.message);
|
|
6518
6555
|
if (parsed.actions.length > 0) {
|
|
6519
6556
|
parsed.actionResults = await executeCCActions(parsed.actions);
|
|
6520
6557
|
}
|
|
@@ -6834,14 +6871,15 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
6834
6871
|
}
|
|
6835
6872
|
|
|
6836
6873
|
// Send final result with actions — execute server-side first
|
|
6837
|
-
|
|
6874
|
+
let { text: displayText, actions, _actionParseError } = parseCCActions(result.text);
|
|
6838
6875
|
// Safety net: detect /loop invocation and convert to create-watch
|
|
6839
|
-
const _loopWatch = _detectLoopInvocation(displayText, actions, toolUses);
|
|
6876
|
+
const _loopWatch = _detectLoopInvocation(displayText, actions, toolUses, body.message);
|
|
6840
6877
|
if (_loopWatch) {
|
|
6841
6878
|
actions.push(_loopWatch);
|
|
6842
6879
|
console.warn('[CC] /loop invocation detected — converted to create-watch');
|
|
6843
6880
|
try { shared.log('warn', '/loop invocation detected in CC response — auto-converted to create-watch'); } catch {}
|
|
6844
6881
|
}
|
|
6882
|
+
actions = _filterImplicitPostDispatchActions(actions, body.message);
|
|
6845
6883
|
let actionResults;
|
|
6846
6884
|
if (actions.length > 0) {
|
|
6847
6885
|
actionResults = await executeCCActions(actions);
|
|
@@ -8471,6 +8509,8 @@ module.exports = {
|
|
|
8471
8509
|
_resolveSkillReadPath,
|
|
8472
8510
|
DOC_CHAT_DOCUMENT_DELIMITER,
|
|
8473
8511
|
_ccValidateAction,
|
|
8512
|
+
_messageExplicitlyRequestsMonitoring,
|
|
8513
|
+
_filterImplicitPostDispatchActions,
|
|
8474
8514
|
_findDuplicateWorkItemCreate: findDuplicateWorkItemCreate,
|
|
8475
8515
|
_createWorkItemWithDedup: createWorkItemWithDedup,
|
|
8476
8516
|
_resolveWorkItemsCreateTarget: resolveWorkItemsCreateTarget,
|
package/engine.js
CHANGED
|
@@ -407,6 +407,50 @@ function mergePendingSteeringEntries(...groups) {
|
|
|
407
407
|
return merged;
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
function promoteCheckpointSteeringForClose(agentId, procInfo, runtime, liveOutputPath) {
|
|
411
|
+
if (!procInfo || procInfo._steeringMessage) return { status: 'none', entries: [] };
|
|
412
|
+
if (runtime?.capabilities?.midRunSessionId !== false) return { status: 'none', entries: [] };
|
|
413
|
+
|
|
414
|
+
const startedAtMs = Date.parse(procInfo.startedAt);
|
|
415
|
+
const runStartMs = Number.isFinite(startedAtMs) ? startedAtMs : 0;
|
|
416
|
+
const pendingPaths = new Set((procInfo._pendingSteeringFiles || []).map(entry => entry?.path || entry).filter(Boolean));
|
|
417
|
+
const deferredPaths = new Set((procInfo._deferredSteeringFiles || []).filter(Boolean));
|
|
418
|
+
const unread = steering.listUnreadSteeringMessages(agentId).filter(entry => entry.message.trim());
|
|
419
|
+
const entriesByPath = new Map(unread.map(entry => [entry.path, entry]));
|
|
420
|
+
const pendingDeferred = Array.from(deferredPaths)
|
|
421
|
+
.map(filePath => entriesByPath.get(filePath))
|
|
422
|
+
.filter(Boolean);
|
|
423
|
+
const lateCheckpoint = unread.filter(entry =>
|
|
424
|
+
entry.createdAtMs >= runStartMs
|
|
425
|
+
&& !pendingPaths.has(entry.path)
|
|
426
|
+
&& !deferredPaths.has(entry.path)
|
|
427
|
+
);
|
|
428
|
+
const checkpointEntries = mergePendingSteeringEntries(pendingDeferred, lateCheckpoint);
|
|
429
|
+
if (checkpointEntries.length === 0) {
|
|
430
|
+
delete procInfo._deferredSteeringFiles;
|
|
431
|
+
return { status: 'none', entries: [] };
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (!procInfo.sessionId) {
|
|
435
|
+
log('warn', `Steering: ${agentId} exited before a resumable sessionId was available - ${checkpointEntries.length} message(s) remain pending`);
|
|
436
|
+
try { fs.appendFileSync(liveOutputPath, `\n[steering-pending] Agent exited before a resumable session was available. Your message remains unread and will be retried on the next dispatch.\n`); } catch {}
|
|
437
|
+
return { status: 'pending', entries: checkpointEntries };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (pendingDeferred.length > 0) {
|
|
441
|
+
log('info', `Steering: delivering ${pendingDeferred.length} deferred message(s) for ${agentId} at resumable checkpoint`);
|
|
442
|
+
}
|
|
443
|
+
if (lateCheckpoint.length > 0) {
|
|
444
|
+
log('info', `Steering: delivering ${lateCheckpoint.length} late checkpoint message(s) for ${agentId} at resumable checkpoint`);
|
|
445
|
+
}
|
|
446
|
+
procInfo._steeringMessage = checkpointEntries.map(entry => entry.message.trim()).join('\n\n');
|
|
447
|
+
procInfo._steeringSessionId = procInfo.sessionId;
|
|
448
|
+
procInfo._steeringEntry = checkpointEntries;
|
|
449
|
+
procInfo._steeringDeferredCheckpoint = true;
|
|
450
|
+
delete procInfo._deferredSteeringFiles;
|
|
451
|
+
return { status: 'promoted', entries: checkpointEntries };
|
|
452
|
+
}
|
|
453
|
+
|
|
410
454
|
// Resolve dependency plan item IDs to their PR branches
|
|
411
455
|
function resolveDependencyBranches(depIds, sourcePlan, project, config) {
|
|
412
456
|
const results = []; // [{ branch, prId }]
|
|
@@ -1193,6 +1237,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1193
1237
|
const MAX_OUTPUT = 1024 * 1024; // 1MB
|
|
1194
1238
|
let stdout = '';
|
|
1195
1239
|
let stderr = '';
|
|
1240
|
+
let steeringAckStdout = '';
|
|
1196
1241
|
const sessionCaptureState = { sessionLineBuffer: '' };
|
|
1197
1242
|
let _trustCheckDone = false;
|
|
1198
1243
|
const _spawnTime = Date.now();
|
|
@@ -1207,6 +1252,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1207
1252
|
const chunk = data.toString();
|
|
1208
1253
|
realActivityMap.set(id, Date.now());
|
|
1209
1254
|
if (stdout.length < MAX_OUTPUT) stdout += chunk.slice(0, MAX_OUTPUT - stdout.length);
|
|
1255
|
+
if (steeringAckStdout.length < MAX_OUTPUT) steeringAckStdout += chunk.slice(0, MAX_OUTPUT - steeringAckStdout.length);
|
|
1210
1256
|
try { fs.appendFileSync(liveOutputPath, chunk); } catch { /* optional */ }
|
|
1211
1257
|
|
|
1212
1258
|
// Trust gate detection: check first 30s of output for trust/permission prompts
|
|
@@ -1254,26 +1300,8 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1254
1300
|
}
|
|
1255
1301
|
|
|
1256
1302
|
const procInfo = activeProcesses.get(id);
|
|
1257
|
-
ackPendingSteeringFiles(agentId, procInfo,
|
|
1258
|
-
|
|
1259
|
-
if (procInfo?._deferredSteeringFiles?.length && procInfo.sessionId) {
|
|
1260
|
-
const deferredPaths = new Set(procInfo._deferredSteeringFiles);
|
|
1261
|
-
const pendingDeferred = steering.listUnreadSteeringMessages(agentId)
|
|
1262
|
-
.filter(entry => deferredPaths.has(entry.path) && entry.message.trim());
|
|
1263
|
-
if (pendingDeferred.length > 0) {
|
|
1264
|
-
log('info', `Steering: delivering ${pendingDeferred.length} deferred message(s) for ${agentId} at resumable checkpoint`);
|
|
1265
|
-
procInfo._steeringMessage = pendingDeferred.map(entry => entry.message.trim()).join('\n\n');
|
|
1266
|
-
procInfo._steeringSessionId = procInfo.sessionId;
|
|
1267
|
-
procInfo._steeringEntry = pendingDeferred;
|
|
1268
|
-
procInfo._steeringDeferredCheckpoint = true;
|
|
1269
|
-
delete procInfo._deferredSteeringFiles;
|
|
1270
|
-
} else {
|
|
1271
|
-
delete procInfo._deferredSteeringFiles;
|
|
1272
|
-
}
|
|
1273
|
-
} else if (procInfo?._deferredSteeringFiles?.length) {
|
|
1274
|
-
log('warn', `Steering: ${agentId} exited before a resumable sessionId was available — message remains pending`);
|
|
1275
|
-
try { fs.appendFileSync(liveOutputPath, `\n[steering-pending] Agent exited before a resumable session was available. Your message remains unread and will be retried on the next dispatch.\n`); } catch {}
|
|
1276
|
-
}
|
|
1303
|
+
ackPendingSteeringFiles(agentId, procInfo, steeringAckStdout);
|
|
1304
|
+
promoteCheckpointSteeringForClose(agentId, procInfo, runtime, liveOutputPath);
|
|
1277
1305
|
|
|
1278
1306
|
// Check if this was a steering kill — re-spawn with resume
|
|
1279
1307
|
if (procInfo?._steeringMessage) {
|
|
@@ -1401,6 +1429,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1401
1429
|
),
|
|
1402
1430
|
});
|
|
1403
1431
|
|
|
1432
|
+
steeringAckStdout = '';
|
|
1404
1433
|
// Live steering kills discard partial old output. Deferred checkpoint
|
|
1405
1434
|
// steering keeps the completed turn output so completion parsing still
|
|
1406
1435
|
// sees the original work if the follow-up only acknowledges steering.
|
|
@@ -1414,6 +1443,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1414
1443
|
const chunk = data.toString();
|
|
1415
1444
|
realActivityMap.set(id, Date.now());
|
|
1416
1445
|
if (stdout.length < MAX_OUTPUT) stdout += chunk.slice(0, MAX_OUTPUT - stdout.length);
|
|
1446
|
+
if (steeringAckStdout.length < MAX_OUTPUT) steeringAckStdout += chunk.slice(0, MAX_OUTPUT - steeringAckStdout.length);
|
|
1417
1447
|
try { fs.appendFileSync(liveOutputPath, chunk); } catch { /* optional */ }
|
|
1418
1448
|
const resumeInfo = activeProcesses.get(id);
|
|
1419
1449
|
markRuntimeResumeOutputSeen(resumeInfo);
|
|
@@ -4719,6 +4749,7 @@ module.exports = {
|
|
|
4719
4749
|
parseConflictFiles, pruneAncestorDeps, preflightMergeSimulation, // exported for testing
|
|
4720
4750
|
isWorktreeRetryableError, removeStaleIndexLock, syncReusedWorktree, // exported for testing
|
|
4721
4751
|
_maxTurnsForType, buildProjectContext, normalizeAc, _buildAgentSpawnFlags, _classifyAgentFailure, // exported for testing
|
|
4752
|
+
promoteCheckpointSteeringForClose, // exported for testing
|
|
4722
4753
|
normalizePrBranch, resolvePrBranch, prCausePart, getPrCauseHead, getPrCauseBase, getPrAutomationCauseKey, getPrAutomationDispatchKey, // exported for testing
|
|
4723
4754
|
|
|
4724
4755
|
// Playbooks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1803",
|
|
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"
|