@yemi33/minions 0.1.2273 → 0.1.2275
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/dashboard/slim/body.html
CHANGED
|
@@ -124,6 +124,17 @@
|
|
|
124
124
|
</div>
|
|
125
125
|
</div>
|
|
126
126
|
</div>
|
|
127
|
+
<!-- Pending work items sub-section: lists work items with
|
|
128
|
+
status === 'pending'. Populated by renderPendingWorkItems() in
|
|
129
|
+
status.js from the /api/work-items poll slice; hidden via the
|
|
130
|
+
`hidden` attribute when there are zero pending items. -->
|
|
131
|
+
<div class="pending-section" id="pending-section" hidden>
|
|
132
|
+
<div class="pending-head">
|
|
133
|
+
Pending Work Items
|
|
134
|
+
<span class="pending-count" id="pending-count"></span>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="pending-list" id="pending-list"></div>
|
|
137
|
+
</div>
|
|
127
138
|
</div>
|
|
128
139
|
</div>
|
|
129
140
|
|
|
@@ -514,6 +514,7 @@
|
|
|
514
514
|
_slimSettle('/api/pull-requests', prev.pullRequests),
|
|
515
515
|
_slimSettle('/api/pinned', prev.pinned),
|
|
516
516
|
_slimSettle('/state/engine/watches.json', prev.watches),
|
|
517
|
+
_slimSettle('/api/work-items', prev.workItems),
|
|
517
518
|
]);
|
|
518
519
|
var data = results[0] || {};
|
|
519
520
|
data.dispatch = (results[1] && typeof results[1] === 'object') ? results[1] : {};
|
|
@@ -521,6 +522,7 @@
|
|
|
521
522
|
data.pullRequests = Array.isArray(results[3]) ? results[3] : [];
|
|
522
523
|
data.pinned = Array.isArray(results[4]) ? results[4] : [];
|
|
523
524
|
data.watches = Array.isArray(results[5]) ? results[5] : [];
|
|
525
|
+
data.workItems = Array.isArray(results[6]) ? results[6] : [];
|
|
524
526
|
applyStatus(data);
|
|
525
527
|
} catch (e) {
|
|
526
528
|
// Soft failure — leave previous values, surface in stamp
|
|
@@ -165,6 +165,11 @@
|
|
|
165
165
|
// ── Team member cards ──────────────────────────────────────
|
|
166
166
|
renderMembers(Array.isArray(data.agents) ? data.agents : []);
|
|
167
167
|
|
|
168
|
+
// ── Pending work items section ─────────────────────────────
|
|
169
|
+
// Surfaces WIs with status === 'pending' (W-mqtx516q). Sourced from the
|
|
170
|
+
// /api/work-items slice merged in by the slim poll (history.js).
|
|
171
|
+
renderPendingWorkItems(Array.isArray(data.workItems) ? data.workItems : []);
|
|
172
|
+
|
|
168
173
|
// ── Stamps + history ───────────────────────────────────────
|
|
169
174
|
var stamp = document.getElementById('cockpit-stamp');
|
|
170
175
|
if (stamp) stamp.textContent = 'updated ' + relTime(data.timestamp || Date.now());
|
|
@@ -174,3 +179,94 @@
|
|
|
174
179
|
if (hStamp) hStamp.textContent = relTime(data.timestamp || Date.now());
|
|
175
180
|
}
|
|
176
181
|
|
|
182
|
+
// ── Pending work items renderer ────────────────────────────────
|
|
183
|
+
// Lists every work item with status === 'pending' in a dedicated section
|
|
184
|
+
// under the cockpit (W-mqtx516q). The section is hidden via the `hidden`
|
|
185
|
+
// attribute when there are zero pending items. Each row shows id, title,
|
|
186
|
+
// project, type, and the engine's _pendingReason as a small badge when set.
|
|
187
|
+
// Everything goes through textContent (no innerHTML) to keep the SEC-03
|
|
188
|
+
// baseline clean. A JSON fingerprint of the visible fields skips the DOM
|
|
189
|
+
// rebuild when nothing changed between 5s polls.
|
|
190
|
+
var _lastPendingJson = null;
|
|
191
|
+
|
|
192
|
+
function renderPendingWorkItems(items) {
|
|
193
|
+
var section = document.getElementById('pending-section');
|
|
194
|
+
var list = document.getElementById('pending-list');
|
|
195
|
+
if (!section || !list) return;
|
|
196
|
+
|
|
197
|
+
var pending = (Array.isArray(items) ? items : []).filter(function(w) {
|
|
198
|
+
return w && w.status === 'pending';
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
var fingerprint = JSON.stringify(pending.map(function(w) {
|
|
202
|
+
return [w.id, w.title, w.project || w._source, w.type, w._pendingReason];
|
|
203
|
+
}));
|
|
204
|
+
if (fingerprint === _lastPendingJson) return;
|
|
205
|
+
_lastPendingJson = fingerprint;
|
|
206
|
+
|
|
207
|
+
if (!pending.length) {
|
|
208
|
+
section.setAttribute('hidden', '');
|
|
209
|
+
list.textContent = '';
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
section.removeAttribute('hidden');
|
|
213
|
+
|
|
214
|
+
var countEl = document.getElementById('pending-count');
|
|
215
|
+
if (countEl) countEl.textContent = String(pending.length);
|
|
216
|
+
|
|
217
|
+
list.textContent = '';
|
|
218
|
+
var frag = document.createDocumentFragment();
|
|
219
|
+
pending.forEach(function(w) {
|
|
220
|
+
var row = document.createElement('div');
|
|
221
|
+
row.className = 'pending-row';
|
|
222
|
+
|
|
223
|
+
// Top line: title + optional pending-reason badge.
|
|
224
|
+
var main = document.createElement('div');
|
|
225
|
+
main.className = 'pending-row-main';
|
|
226
|
+
|
|
227
|
+
var title = document.createElement('span');
|
|
228
|
+
title.className = 'pending-title';
|
|
229
|
+
title.textContent = w.title || w.id || '(untitled)';
|
|
230
|
+
title.title = (w.title || w.id || '') + (w.id ? ' (' + w.id + ')' : '');
|
|
231
|
+
main.appendChild(title);
|
|
232
|
+
|
|
233
|
+
if (w._pendingReason) {
|
|
234
|
+
var badge = document.createElement('span');
|
|
235
|
+
badge.className = 'pending-reason-badge';
|
|
236
|
+
badge.textContent = String(w._pendingReason).replace(/_/g, ' ');
|
|
237
|
+
badge.title = 'Pending reason: ' + w._pendingReason;
|
|
238
|
+
main.appendChild(badge);
|
|
239
|
+
}
|
|
240
|
+
row.appendChild(main);
|
|
241
|
+
|
|
242
|
+
// Meta line: id + project + type chips.
|
|
243
|
+
var meta = document.createElement('div');
|
|
244
|
+
meta.className = 'pending-row-meta';
|
|
245
|
+
|
|
246
|
+
var idEl = document.createElement('span');
|
|
247
|
+
idEl.className = 'pending-meta-id';
|
|
248
|
+
idEl.textContent = w.id || '';
|
|
249
|
+
meta.appendChild(idEl);
|
|
250
|
+
|
|
251
|
+
var project = w.project || w._source;
|
|
252
|
+
if (project) {
|
|
253
|
+
var projEl = document.createElement('span');
|
|
254
|
+
projEl.className = 'pending-meta-chip';
|
|
255
|
+
projEl.textContent = project;
|
|
256
|
+
meta.appendChild(projEl);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (w.type) {
|
|
260
|
+
var typeEl = document.createElement('span');
|
|
261
|
+
typeEl.className = 'pending-meta-chip pending-meta-type';
|
|
262
|
+
typeEl.textContent = w.type;
|
|
263
|
+
meta.appendChild(typeEl);
|
|
264
|
+
}
|
|
265
|
+
row.appendChild(meta);
|
|
266
|
+
|
|
267
|
+
frag.appendChild(row);
|
|
268
|
+
});
|
|
269
|
+
list.appendChild(frag);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
@@ -1139,6 +1139,93 @@
|
|
|
1139
1139
|
.team-section { margin-bottom: 14px; }
|
|
1140
1140
|
.cockpit-section { padding-top: 14px; border-top: 1px solid var(--border); }
|
|
1141
1141
|
|
|
1142
|
+
/* Pending work items section (W-mqtx516q). Sits under the cockpit tiles;
|
|
1143
|
+
hidden via the `hidden` attribute when there are no pending WIs. */
|
|
1144
|
+
.pending-section {
|
|
1145
|
+
padding-top: 14px;
|
|
1146
|
+
margin-top: 14px;
|
|
1147
|
+
border-top: 1px solid var(--border);
|
|
1148
|
+
}
|
|
1149
|
+
.pending-section[hidden] { display: none; }
|
|
1150
|
+
.pending-head {
|
|
1151
|
+
font-size: var(--text-sm);
|
|
1152
|
+
font-weight: 600;
|
|
1153
|
+
color: var(--text);
|
|
1154
|
+
letter-spacing: 0.02em;
|
|
1155
|
+
text-transform: uppercase;
|
|
1156
|
+
margin-bottom: 8px;
|
|
1157
|
+
display: flex;
|
|
1158
|
+
align-items: center;
|
|
1159
|
+
gap: 6px;
|
|
1160
|
+
}
|
|
1161
|
+
.pending-count {
|
|
1162
|
+
font-size: var(--text-xs);
|
|
1163
|
+
font-weight: 600;
|
|
1164
|
+
color: var(--orange);
|
|
1165
|
+
background: rgba(214, 153, 34, 0.14);
|
|
1166
|
+
border-radius: 9px;
|
|
1167
|
+
padding: 0 7px;
|
|
1168
|
+
line-height: 16px;
|
|
1169
|
+
}
|
|
1170
|
+
.pending-list {
|
|
1171
|
+
display: flex;
|
|
1172
|
+
flex-direction: column;
|
|
1173
|
+
gap: 6px;
|
|
1174
|
+
}
|
|
1175
|
+
.pending-row {
|
|
1176
|
+
background: var(--surface2);
|
|
1177
|
+
border: 1px solid var(--border);
|
|
1178
|
+
border-left: 3px solid var(--orange);
|
|
1179
|
+
border-radius: var(--radius);
|
|
1180
|
+
padding: 7px 9px;
|
|
1181
|
+
}
|
|
1182
|
+
.pending-row-main {
|
|
1183
|
+
display: flex;
|
|
1184
|
+
align-items: center;
|
|
1185
|
+
gap: 6px;
|
|
1186
|
+
}
|
|
1187
|
+
.pending-title {
|
|
1188
|
+
font-size: var(--text-md);
|
|
1189
|
+
color: var(--text);
|
|
1190
|
+
overflow: hidden;
|
|
1191
|
+
text-overflow: ellipsis;
|
|
1192
|
+
white-space: nowrap;
|
|
1193
|
+
min-width: 0;
|
|
1194
|
+
flex: 1;
|
|
1195
|
+
}
|
|
1196
|
+
.pending-reason-badge {
|
|
1197
|
+
flex: none;
|
|
1198
|
+
font-size: var(--text-xs);
|
|
1199
|
+
color: var(--orange);
|
|
1200
|
+
border: 1px solid rgba(214, 153, 34, 0.5);
|
|
1201
|
+
border-radius: 6px;
|
|
1202
|
+
padding: 0 5px;
|
|
1203
|
+
line-height: 15px;
|
|
1204
|
+
white-space: nowrap;
|
|
1205
|
+
}
|
|
1206
|
+
.pending-row-meta {
|
|
1207
|
+
display: flex;
|
|
1208
|
+
align-items: center;
|
|
1209
|
+
flex-wrap: wrap;
|
|
1210
|
+
gap: 6px;
|
|
1211
|
+
margin-top: 4px;
|
|
1212
|
+
}
|
|
1213
|
+
.pending-meta-id {
|
|
1214
|
+
font-size: var(--text-xs);
|
|
1215
|
+
color: var(--muted);
|
|
1216
|
+
font-family: var(--mono, monospace);
|
|
1217
|
+
}
|
|
1218
|
+
.pending-meta-chip {
|
|
1219
|
+
font-size: var(--text-xs);
|
|
1220
|
+
color: var(--muted);
|
|
1221
|
+
background: var(--surface);
|
|
1222
|
+
border: 1px solid var(--border);
|
|
1223
|
+
border-radius: 6px;
|
|
1224
|
+
padding: 0 6px;
|
|
1225
|
+
line-height: 15px;
|
|
1226
|
+
}
|
|
1227
|
+
.pending-meta-type { color: var(--blue); }
|
|
1228
|
+
|
|
1142
1229
|
/* Square member cards. Auto-fill so the column count adapts to the
|
|
1143
1230
|
panel width; aspect-ratio keeps each card square regardless. */
|
|
1144
1231
|
.member-grid {
|
|
@@ -168,14 +168,19 @@ async function validateAcceptanceCriteria(workItem, opts = {}) {
|
|
|
168
168
|
model: _resolveModel(opts),
|
|
169
169
|
maxTurns: 1,
|
|
170
170
|
direct: true,
|
|
171
|
-
// Direct callLLM spawns the runtime CLI with
|
|
172
|
-
// _spawnProcess).
|
|
173
|
-
// Edit/Write/Bash
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
|
|
171
|
+
// No tools at all. Direct callLLM spawns the runtime CLI with
|
|
172
|
+
// cwd=MINIONS_DIR (engine/llm.js _spawnProcess). P-b5e2a481 (2026-06-01
|
|
173
|
+
// 23:44 UTC) removed write access (Edit/Write/Bash) after the validator
|
|
174
|
+
// "previewed" work by editing dashboard/js/refresh.js in MINIONS_DIR, but
|
|
175
|
+
// left Read/Grep/Glob in place. Those read tools made the model search the
|
|
176
|
+
// MINIONS_DIR codebase to "verify" whether the described code exists — but
|
|
177
|
+
// the cwd is always MINIONS_DIR, never the target project, so work items
|
|
178
|
+
// targeting other repos (e.g. Android in office/src) were searched against
|
|
179
|
+
// this Node.js codebase, found nothing, and were wrongly marked invalid.
|
|
180
|
+
// The pre-dispatch eval is a pure text-reasoning call ("is this clear,
|
|
181
|
+
// actionable, and testable?") and needs no filesystem access. Passing no
|
|
182
|
+
// allowedTools makes the runtime omit --allowedTools entirely so the model
|
|
183
|
+
// reasons only over the prompt text.
|
|
179
184
|
engineConfig: opts.engineConfig,
|
|
180
185
|
});
|
|
181
186
|
} catch (e) {
|
package/engine/queries.js
CHANGED
|
@@ -1977,6 +1977,18 @@ function getWorkItems(config, opts) {
|
|
|
1977
1977
|
// Use snapshot — sync access; cold start before any async warm returns [].
|
|
1978
1978
|
// Best-effort enrichment for work item _artifacts.notes, not correctness-critical.
|
|
1979
1979
|
const _kbEntries = getKnowledgeBaseEntriesSnapshot();
|
|
1980
|
+
// PERF (dashboard event-loop freeze fix): the notes-linkage match below is
|
|
1981
|
+
// `entry contains agentId AND entry contains itemId`. Filtering the FULL
|
|
1982
|
+
// _kbEntries (can be ~10k) and _archiveFiles (~2k) per work item is O(items ×
|
|
1983
|
+
// files) — on a busy instance that's millions of String.includes() per
|
|
1984
|
+
// /api/work-items call, run synchronously, which hard-freezes the single
|
|
1985
|
+
// dashboard event loop. Bucket the two large collections by agentId ONCE per
|
|
1986
|
+
// distinct agent (lazily, like _agentDirCache above), then each item only
|
|
1987
|
+
// scans its agent's much-smaller bucket for the itemId. Exact same match
|
|
1988
|
+
// semantics; O(agents × files) one-time + O(items × bucket) instead of
|
|
1989
|
+
// O(items × files). _inboxFiles stays a direct filter (tiny — a handful).
|
|
1990
|
+
const _kbByAgent = {}; // agentId → kb entries whose source includes agentId
|
|
1991
|
+
const _archiveByAgent = {}; // agentId → archive filenames that include agentId
|
|
1980
1992
|
// P-34fa5d79 — pre-build the wiId → notes map so each item's _notes lookup
|
|
1981
1993
|
// is O(1) instead of re-scanning inbox+archive per item.
|
|
1982
1994
|
const _notesByWi = _buildNotesByWiMap();
|
|
@@ -1996,14 +2008,25 @@ function getWorkItems(config, opts) {
|
|
|
1996
2008
|
// Notes: inbox → KB (via source field) → archive (fallback if KB was swept)
|
|
1997
2009
|
const itemId = item.id || '___';
|
|
1998
2010
|
const matchInbox = _inboxFiles.filter(f => f.includes(agentId) && f.includes(itemId));
|
|
1999
|
-
|
|
2011
|
+
// Lazily bucket KB by agentId (computed once per distinct agent), then
|
|
2012
|
+
// filter the small bucket by itemId — same result as filtering all of
|
|
2013
|
+
// _kbEntries by (agentId AND itemId), without the per-item full scan.
|
|
2014
|
+
if (_kbByAgent[agentId] === undefined) {
|
|
2015
|
+
_kbByAgent[agentId] = _kbEntries.filter(kb => kb.source && kb.source.includes(agentId));
|
|
2016
|
+
}
|
|
2017
|
+
const matchKb = _kbByAgent[agentId].filter(kb => kb.source.includes(itemId));
|
|
2000
2018
|
const allNotes = [
|
|
2001
2019
|
...matchInbox,
|
|
2002
2020
|
...matchKb.map(kb => 'kb:' + kb.cat + '/' + kb.file),
|
|
2003
2021
|
];
|
|
2004
|
-
// Archive fallback — only if nothing found in inbox or KB
|
|
2022
|
+
// Archive fallback — only if nothing found in inbox or KB. Same agentId
|
|
2023
|
+
// bucketing as KB so the (potentially large) archive list isn't fully
|
|
2024
|
+
// re-scanned per item.
|
|
2005
2025
|
if (allNotes.length === 0) {
|
|
2006
|
-
|
|
2026
|
+
if (_archiveByAgent[agentId] === undefined) {
|
|
2027
|
+
_archiveByAgent[agentId] = _archiveFiles.filter(f => f.includes(agentId));
|
|
2028
|
+
}
|
|
2029
|
+
const matchArchive = _archiveByAgent[agentId].filter(f => f.includes(itemId));
|
|
2007
2030
|
for (const f of matchArchive) allNotes.push('archive:' + f);
|
|
2008
2031
|
}
|
|
2009
2032
|
if (allNotes.length > 0) arts.notes = _mergeArtifactNotes(arts.notes, allNotes);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2275",
|
|
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"
|