@yemi33/minions 0.1.146 β 0.1.148
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 +17 -0
- package/dashboard/js/command-center.js +59 -0
- package/dashboard/js/render-work-items.js +3 -0
- package/dashboard/layout.html +1 -0
- package/dashboard/styles.css +7 -0
- package/engine/lifecycle.js +41 -0
- package/engine/shared.js +1 -0
- package/engine.js +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.148 (2026-04-01)
|
|
4
|
+
|
|
5
|
+
### Engine
|
|
6
|
+
- engine.js
|
|
7
|
+
- engine/lifecycle.js
|
|
8
|
+
- engine/shared.js
|
|
9
|
+
|
|
10
|
+
### Dashboard
|
|
11
|
+
- dashboard/js/command-center.js
|
|
12
|
+
- dashboard/js/render-work-items.js
|
|
13
|
+
- dashboard/layout.html
|
|
14
|
+
- dashboard/styles.css
|
|
15
|
+
|
|
16
|
+
### Other
|
|
17
|
+
- .github/workflows/pr-tests.yml
|
|
18
|
+
- test/unit.test.js
|
|
19
|
+
|
|
3
20
|
## 0.1.146 (2026-04-01)
|
|
4
21
|
|
|
5
22
|
### Engine
|
|
@@ -9,6 +9,7 @@ let _ccQueue = [];
|
|
|
9
9
|
function toggleCommandCenter() {
|
|
10
10
|
_ccOpen = !_ccOpen;
|
|
11
11
|
const drawer = document.getElementById('cc-drawer');
|
|
12
|
+
if (_ccOpen) ccApplySavedWidth();
|
|
12
13
|
drawer.style.display = _ccOpen ? 'flex' : 'none';
|
|
13
14
|
if (_ccOpen) {
|
|
14
15
|
clearNotifBadge(document.getElementById('cc-toggle-btn'));
|
|
@@ -462,4 +463,62 @@ async function ccExecuteAction(action) {
|
|
|
462
463
|
refresh();
|
|
463
464
|
}
|
|
464
465
|
|
|
466
|
+
// --- CC Resize Logic ---
|
|
467
|
+
const CC_MIN_WIDTH = 320;
|
|
468
|
+
const CC_MAX_WIDTH_RATIO = 0.8; // 80% of viewport
|
|
469
|
+
const CC_DEFAULT_WIDTH = 420;
|
|
470
|
+
const CC_WIDTH_KEY = 'cc-drawer-width';
|
|
471
|
+
|
|
472
|
+
function ccApplySavedWidth() {
|
|
473
|
+
const drawer = document.getElementById('cc-drawer');
|
|
474
|
+
if (!drawer) return;
|
|
475
|
+
const saved = parseInt(localStorage.getItem(CC_WIDTH_KEY), 10);
|
|
476
|
+
if (saved && saved >= CC_MIN_WIDTH) {
|
|
477
|
+
const maxW = Math.floor(window.innerWidth * CC_MAX_WIDTH_RATIO);
|
|
478
|
+
drawer.style.width = Math.min(saved, maxW) + 'px';
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function ccInitResize() {
|
|
483
|
+
const handle = document.getElementById('cc-resize-handle');
|
|
484
|
+
const drawer = document.getElementById('cc-drawer');
|
|
485
|
+
if (!handle || !drawer) return;
|
|
486
|
+
|
|
487
|
+
let startX = 0;
|
|
488
|
+
let startW = 0;
|
|
489
|
+
|
|
490
|
+
function onMouseMove(e) {
|
|
491
|
+
const maxW = Math.floor(window.innerWidth * CC_MAX_WIDTH_RATIO);
|
|
492
|
+
const delta = startX - e.clientX; // dragging left = wider
|
|
493
|
+
const newW = Math.max(CC_MIN_WIDTH, Math.min(startW + delta, maxW));
|
|
494
|
+
drawer.style.width = newW + 'px';
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function onMouseUp() {
|
|
498
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
499
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
500
|
+
document.body.classList.remove('cc-resizing');
|
|
501
|
+
handle.classList.remove('active');
|
|
502
|
+
// Persist
|
|
503
|
+
try { localStorage.setItem(CC_WIDTH_KEY, parseInt(drawer.style.width, 10)); } catch {}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
handle.addEventListener('mousedown', (e) => {
|
|
507
|
+
e.preventDefault();
|
|
508
|
+
startX = e.clientX;
|
|
509
|
+
startW = drawer.offsetWidth;
|
|
510
|
+
document.body.classList.add('cc-resizing');
|
|
511
|
+
handle.classList.add('active');
|
|
512
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
513
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Init resize on DOM ready
|
|
518
|
+
if (document.readyState === 'loading') {
|
|
519
|
+
document.addEventListener('DOMContentLoaded', ccInitResize);
|
|
520
|
+
} else {
|
|
521
|
+
ccInitResize();
|
|
522
|
+
}
|
|
523
|
+
|
|
465
524
|
window.MinionsCC = { toggleCommandCenter, ccNewSession, ccRestoreMessages, ccSaveState, ccUpdateSessionIndicator, ccAddMessage, ccSend, ccExecuteAction };
|
|
@@ -388,6 +388,9 @@ function openWorkItemDetail(id) {
|
|
|
388
388
|
if (item.references?.length) html += field('References', item.references.map(r => '<a href="' + escHtml(r.url) + '" target="_blank" style="color:var(--blue)">' + escHtml(r.title || r.url) + '</a>' + (r.type ? ' <span style="color:var(--muted);font-size:10px">(' + escHtml(r.type) + ')</span>' : '')).join('<br>'));
|
|
389
389
|
if (item._humanFeedback) html += field('Human Feedback', (item._humanFeedback.rating === 'up' ? 'π' : 'π') + (item._humanFeedback.comment ? ' β ' + escHtml(item._humanFeedback.comment) : ''));
|
|
390
390
|
if (item._pr) html += field('Pull Request', '<a href="' + escHtml(item._prUrl || '#') + '" target="_blank" style="color:var(--blue)">' + escHtml(item._pr) + '</a>');
|
|
391
|
+
if (item._totalCostUsd != null) html += field('Cumulative Cost', '$' + Number(item._totalCostUsd).toFixed(4));
|
|
392
|
+
if (item._totalInputTokens) html += field('Total Input Tokens', Number(item._totalInputTokens).toLocaleString());
|
|
393
|
+
if (item._totalOutputTokens) html += field('Total Output Tokens', Number(item._totalOutputTokens).toLocaleString());
|
|
391
394
|
html += '</div>';
|
|
392
395
|
|
|
393
396
|
document.getElementById('modal-title').textContent = item.title || item.id;
|
package/dashboard/layout.html
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
<!-- Command Center Drawer -->
|
|
24
24
|
<div id="cc-drawer" style="display:none;position:fixed;top:0;right:0;bottom:0;width:420px;background:var(--surface);border-left:1px solid var(--border);z-index:300;flex-direction:column">
|
|
25
|
+
<div id="cc-resize-handle" class="cc-resize-handle"></div>
|
|
25
26
|
<div style="padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between">
|
|
26
27
|
<div style="display:flex;align-items:center;gap:8px">
|
|
27
28
|
<span style="font-weight:700;color:var(--blue);font-size:14px">Command Center</span>
|
package/dashboard/styles.css
CHANGED
|
@@ -581,6 +581,13 @@
|
|
|
581
581
|
.notif-badge.processing span:nth-child(2) { animation-delay: 0.2s; }
|
|
582
582
|
.notif-badge.processing span:nth-child(3) { animation-delay: 0.4s; }
|
|
583
583
|
@keyframes notifPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
584
|
+
|
|
585
|
+
/* Command Center resize handle */
|
|
586
|
+
.cc-resize-handle { position: absolute; top: 0; left: -3px; bottom: 0; width: 6px; cursor: col-resize; z-index: 301; }
|
|
587
|
+
.cc-resize-handle::after { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 2px; height: 32px; background: var(--border); border-radius: 2px; opacity: 0; transition: opacity 0.15s; }
|
|
588
|
+
.cc-resize-handle:hover::after, .cc-resize-handle.active::after { opacity: 1; background: var(--blue); }
|
|
589
|
+
body.cc-resizing { cursor: col-resize !important; user-select: none !important; }
|
|
590
|
+
|
|
584
591
|
.modal-body { padding: var(--space-7) var(--space-8); overflow-y: auto; overflow-x: auto; white-space: pre-wrap; font-size: var(--text-md); line-height: 1.7; color: var(--muted); font-family: Consolas, monospace; }
|
|
585
592
|
|
|
586
593
|
/* Responsive: tablet / narrow window */
|
package/engine/lifecycle.js
CHANGED
|
@@ -1153,6 +1153,47 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
|
|
|
1153
1153
|
} catch (err) { log('warn', `Session save: ${err.message}`); }
|
|
1154
1154
|
}
|
|
1155
1155
|
|
|
1156
|
+
// ββ Accumulate per-work-item cost tracking ββββββββββββββββββββββββββββββββββ
|
|
1157
|
+
if (taskUsage && meta?.item?.id) {
|
|
1158
|
+
try {
|
|
1159
|
+
const wiPath = meta.source === 'central-work-item' || meta.source === 'central-work-item-fanout'
|
|
1160
|
+
? path.join(MINIONS_DIR, 'work-items.json')
|
|
1161
|
+
: meta.project?.name ? path.join(MINIONS_DIR, 'projects', meta.project.name, 'work-items.json') : null;
|
|
1162
|
+
if (wiPath) {
|
|
1163
|
+
mutateJsonFileLocked(wiPath, (items) => {
|
|
1164
|
+
if (!Array.isArray(items)) return items;
|
|
1165
|
+
const wi = items.find(i => i.id === meta.item.id);
|
|
1166
|
+
if (wi) {
|
|
1167
|
+
wi._totalCostUsd = (wi._totalCostUsd || 0) + (taskUsage.costUsd || 0);
|
|
1168
|
+
wi._totalInputTokens = (wi._totalInputTokens || 0) + (taskUsage.inputTokens || 0);
|
|
1169
|
+
wi._totalOutputTokens = (wi._totalOutputTokens || 0) + (taskUsage.outputTokens || 0);
|
|
1170
|
+
}
|
|
1171
|
+
return items;
|
|
1172
|
+
}, { defaultValue: [] });
|
|
1173
|
+
|
|
1174
|
+
// Cost ceiling circuit breaker β treat like evalMaxIterations exceeded
|
|
1175
|
+
const engineCfg = config?.engine || {};
|
|
1176
|
+
const evalMaxCost = engineCfg.evalMaxCost != null ? engineCfg.evalMaxCost : shared.ENGINE_DEFAULTS.evalMaxCost;
|
|
1177
|
+
if (evalMaxCost != null && evalMaxCost > 0) {
|
|
1178
|
+
const freshItems = safeJson(wiPath) || [];
|
|
1179
|
+
const wi = freshItems.find(i => i.id === meta.item.id);
|
|
1180
|
+
if (wi && wi._totalCostUsd > evalMaxCost && wi.status !== 'needs-human-review') {
|
|
1181
|
+
mutateJsonFileLocked(wiPath, (items) => {
|
|
1182
|
+
if (!Array.isArray(items)) return items;
|
|
1183
|
+
const target = items.find(i => i.id === meta.item.id);
|
|
1184
|
+
if (target) {
|
|
1185
|
+
target.status = 'needs-human-review';
|
|
1186
|
+
target.failReason = `Cumulative cost $${wi._totalCostUsd.toFixed(2)} exceeds evalMaxCost ceiling $${evalMaxCost.toFixed(2)}`;
|
|
1187
|
+
log('warn', `Work item ${meta.item.id} exceeded cost ceiling ($${wi._totalCostUsd.toFixed(2)} > $${evalMaxCost.toFixed(2)}) β needs-human-review`);
|
|
1188
|
+
}
|
|
1189
|
+
return items;
|
|
1190
|
+
}, { defaultValue: [] });
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
} catch (err) { log('warn', `Cost accumulation: ${err.message}`); }
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1156
1197
|
// Handle decomposition results β create sub-items from decompose agent output
|
|
1157
1198
|
let skipDoneStatus = false;
|
|
1158
1199
|
if (type === 'decompose' && isSuccess && meta?.item?.id) {
|
package/engine/shared.js
CHANGED
|
@@ -358,6 +358,7 @@ const ENGINE_DEFAULTS = {
|
|
|
358
358
|
meetingRoundTimeout: 600000, // 10min per meeting round before auto-advance
|
|
359
359
|
evalLoop: true, // enable evaluateβfix loop after implementation completes
|
|
360
360
|
evalMaxIterations: 3, // max evaluateβfix cycles before escalating to human
|
|
361
|
+
evalMaxCost: null, // USD ceiling per work item across all eval iterations; null = no limit (gather baseline data first)
|
|
361
362
|
};
|
|
362
363
|
|
|
363
364
|
const DEFAULT_AGENTS = {
|
package/engine.js
CHANGED
|
@@ -1131,7 +1131,7 @@ function materializePlansAsWorkItems(config) {
|
|
|
1131
1131
|
|
|
1132
1132
|
const id = item.id; // Work item ID = PRD item ID β no indirection
|
|
1133
1133
|
const complexity = item.estimated_complexity || 'medium';
|
|
1134
|
-
const criteria = (item.acceptance_criteria
|
|
1134
|
+
const criteria = (Array.isArray(item.acceptance_criteria) ? item.acceptance_criteria : []).map(c => `- ${c}`).join('\n');
|
|
1135
1135
|
|
|
1136
1136
|
const newItem = {
|
|
1137
1137
|
id,
|
|
@@ -1471,7 +1471,7 @@ function discoverFromWorkItems(config, project) {
|
|
|
1471
1471
|
'- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
|
|
1472
1472
|
).join('\n');
|
|
1473
1473
|
vars.references = refs ? '## References\n\n' + refs : '';
|
|
1474
|
-
const ac = (item.acceptanceCriteria
|
|
1474
|
+
const ac = (Array.isArray(item.acceptanceCriteria) ? item.acceptanceCriteria : []).map(c => '- [ ] ' + c).join('\n');
|
|
1475
1475
|
vars.acceptance_criteria = ac ? '## Acceptance Criteria\n\n' + ac : '';
|
|
1476
1476
|
|
|
1477
1477
|
// Inject checkpoint context if agent left a checkpoint.json from a prior run
|
|
@@ -1806,7 +1806,7 @@ function discoverCentralWorkItems(config) {
|
|
|
1806
1806
|
'- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
|
|
1807
1807
|
).join('\n');
|
|
1808
1808
|
vars.references = fanRefs ? '## References\n\n' + fanRefs : '';
|
|
1809
|
-
const fanAc = (item.acceptanceCriteria
|
|
1809
|
+
const fanAc = (Array.isArray(item.acceptanceCriteria) ? item.acceptanceCriteria : []).map(c => '- [ ] ' + c).join('\n');
|
|
1810
1810
|
vars.acceptance_criteria = fanAc ? '## Acceptance Criteria\n\n' + fanAc : '';
|
|
1811
1811
|
|
|
1812
1812
|
if (workType === 'ask') {
|
|
@@ -1888,7 +1888,7 @@ function discoverCentralWorkItems(config) {
|
|
|
1888
1888
|
'- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
|
|
1889
1889
|
).join('\n');
|
|
1890
1890
|
vars.references = normRefs ? '## References\n\n' + normRefs : '';
|
|
1891
|
-
const normAc = (item.acceptanceCriteria
|
|
1891
|
+
const normAc = (Array.isArray(item.acceptanceCriteria) ? item.acceptanceCriteria : []).map(c => '- [ ] ' + c).join('\n');
|
|
1892
1892
|
vars.acceptance_criteria = normAc ? '## Acceptance Criteria\n\n' + normAc : '';
|
|
1893
1893
|
|
|
1894
1894
|
// Inject checkpoint context if agent left a checkpoint.json from a prior run
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.148",
|
|
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"
|