@yemi33/minions 0.1.1605 → 0.1.1607
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/render-utils.js +75 -3
- package/dashboard/js/settings.js +6 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.1607 (2026-04-28)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
6
|
- match runtime tags to actual logos (pixel-crab Claude, mascot Copilot)
|
|
7
7
|
- replace runtime text tag with inline SVG logos
|
|
8
8
|
|
|
9
9
|
### Fixes
|
|
10
|
+
- gate live completion banner on final process exit
|
|
11
|
+
- wrap CC Model input so its label/hint survive runtime hydration
|
|
10
12
|
- preserve reconnect stream state
|
|
11
13
|
- preserve copilot cc action blocks
|
|
12
14
|
- improve copilot command center streaming
|
|
@@ -52,6 +52,12 @@ function formatToolSummary(name, input) {
|
|
|
52
52
|
/**
|
|
53
53
|
* Internal helper: renders a single parsed JSON object into an HTML fragment.
|
|
54
54
|
* @param {object} obj - Parsed JSON object from agent JSONL output
|
|
55
|
+
* @param {object} [state] - Mutable render state shared across calls in a single
|
|
56
|
+
* renderAgentOutput pass. Persistent fields: copilotToolKeys (Set), copilotDeltaBuffer,
|
|
57
|
+
* copilotReasoningPending. Per-line fields written by the caller before each call:
|
|
58
|
+
* isFinalResult (true only for the LAST result line before [process-exit]; gates the
|
|
59
|
+
* completion banner so ScheduleWakeup resume cycles don't repeatedly fire it),
|
|
60
|
+
* exitInfo ({ code, success } parsed from [process-exit], or null).
|
|
55
61
|
* @returns {string} HTML fragment
|
|
56
62
|
*/
|
|
57
63
|
function _renderJsonObj(obj, state) {
|
|
@@ -161,7 +167,19 @@ function _renderJsonObj(obj, state) {
|
|
|
161
167
|
}
|
|
162
168
|
|
|
163
169
|
if (obj.type === 'result') {
|
|
164
|
-
|
|
170
|
+
// Banner is gated on TWO conditions: (a) this is the LAST result line in the output,
|
|
171
|
+
// and (b) the [process-exit] sentinel has been seen. Intermediate result lines from
|
|
172
|
+
// ScheduleWakeup resume cycles fall through and render nothing.
|
|
173
|
+
if (state.isFinalResult) {
|
|
174
|
+
var subtype = typeof obj.subtype === 'string' ? obj.subtype : '';
|
|
175
|
+
var resultIsError = obj.is_error === true || subtype.startsWith('error');
|
|
176
|
+
var exitFailed = state.exitInfo && state.exitInfo.success === false;
|
|
177
|
+
if (resultIsError || exitFailed) {
|
|
178
|
+
parts.push('<div style="background:rgba(248,81,73,0.1);border:1px solid var(--red);padding:8px 12px;border-radius:8px;margin:8px 0;font-size:12px;color:var(--red)">\u2717 Task ended with error</div>');
|
|
179
|
+
} else {
|
|
180
|
+
parts.push('<div style="background:rgba(63,185,80,0.1);border:1px solid var(--green);padding:8px 12px;border-radius:8px;margin:8px 0;font-size:12px;color:var(--green)">\u2713 Task complete</div>');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
165
183
|
}
|
|
166
184
|
|
|
167
185
|
return parts.join('');
|
|
@@ -197,6 +215,47 @@ function renderAgentOutput(text) {
|
|
|
197
215
|
}
|
|
198
216
|
}
|
|
199
217
|
|
|
218
|
+
// ── Pre-scan ──────────────────────────────────────────────────────────────
|
|
219
|
+
// ScheduleWakeup-based polling agents emit one `"type":"result"` JSONL line
|
|
220
|
+
// per resume cycle, followed by another resume — only the LAST result line
|
|
221
|
+
// before spawn-agent.js writes its `[process-exit]` sentinel is the true
|
|
222
|
+
// final result. Intermediate result lines must NOT trigger the banner.
|
|
223
|
+
//
|
|
224
|
+
// spawn-agent.js writes:
|
|
225
|
+
// "\n[process-exit] code=N\n" on normal close (engine/spawn-agent.js:202)
|
|
226
|
+
// "\n[process-exit] spawn-failed\n" on synchronous spawn() throw
|
|
227
|
+
//
|
|
228
|
+
// We pre-scan to find: (1) whether [process-exit] was emitted at all, (2) its
|
|
229
|
+
// exit code (success vs failure), and (3) the line index of the last result
|
|
230
|
+
// strictly before that sentinel.
|
|
231
|
+
var exitInfo = null; // null = process still running (no banner ever fires)
|
|
232
|
+
var exitLineIdx = -1;
|
|
233
|
+
var lastResultLineIdx = -1;
|
|
234
|
+
var exitRe = /^\[process-exit\]\s+(?:code=)?(-?\d+|spawn-failed)\s*$/;
|
|
235
|
+
for (var k = 0; k < lines.length; k++) {
|
|
236
|
+
var t = lines[k].trim();
|
|
237
|
+
if (!t) continue;
|
|
238
|
+
var em = exitRe.exec(t);
|
|
239
|
+
if (em) {
|
|
240
|
+
var token = em[1];
|
|
241
|
+
var code = token === 'spawn-failed' ? -1 : parseInt(token, 10);
|
|
242
|
+
exitInfo = { code: code, success: code === 0 };
|
|
243
|
+
exitLineIdx = k;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (exitLineIdx !== -1) {
|
|
248
|
+
for (var r = 0; r < exitLineIdx; r++) {
|
|
249
|
+
var rt = lines[r].trim();
|
|
250
|
+
if (!rt || rt.charCodeAt(0) !== 123 /* '{' */) continue;
|
|
251
|
+
try {
|
|
252
|
+
var probe = JSON.parse(rt);
|
|
253
|
+
if (probe && probe.type === 'result') lastResultLineIdx = r;
|
|
254
|
+
} catch (e) { /* ignore parse errors during scan */ }
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
state.exitInfo = exitInfo;
|
|
258
|
+
|
|
200
259
|
for (var i = 0; i < lines.length; i++) {
|
|
201
260
|
var trimmed = lines[i].trim();
|
|
202
261
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
@@ -204,6 +263,9 @@ function renderAgentOutput(text) {
|
|
|
204
263
|
// Heartbeat — filter out
|
|
205
264
|
if (trimmed.startsWith('[heartbeat]')) continue;
|
|
206
265
|
|
|
266
|
+
// Process-exit sentinel — internal signal, never displayed
|
|
267
|
+
if (trimmed.startsWith('[process-exit]')) continue;
|
|
268
|
+
|
|
207
269
|
// Human steering
|
|
208
270
|
if (trimmed.startsWith('[human-steering]')) {
|
|
209
271
|
var msg = trimmed.replace('[human-steering] ', '');
|
|
@@ -220,24 +282,26 @@ function renderAgentOutput(text) {
|
|
|
220
282
|
continue;
|
|
221
283
|
}
|
|
222
284
|
|
|
223
|
-
// JSON array line
|
|
285
|
+
// JSON array line — never holds the canonical result, banner never fires here
|
|
224
286
|
if (trimmed.startsWith('[')) {
|
|
225
287
|
try {
|
|
226
288
|
var arr = JSON.parse(trimmed);
|
|
227
289
|
if (Array.isArray(arr)) {
|
|
290
|
+
state.isFinalResult = false;
|
|
228
291
|
for (var j = 0; j < arr.length; j++) fragments.push(_renderJsonObj(arr[j], state));
|
|
229
292
|
continue;
|
|
230
293
|
}
|
|
231
294
|
} catch (e) { /* fall through */ }
|
|
232
295
|
}
|
|
233
296
|
|
|
234
|
-
// JSON object line
|
|
297
|
+
// JSON object line — banner fires only when this is the final result AND process exited
|
|
235
298
|
if (trimmed.startsWith('{')) {
|
|
236
299
|
try {
|
|
237
300
|
var obj = JSON.parse(trimmed);
|
|
238
301
|
if (obj.type !== 'assistant.message_delta' && obj.type !== 'assistant.reasoning' && obj.type !== 'assistant.reasoning_delta' && obj.type !== 'assistant.message') {
|
|
239
302
|
flushCopilotPending();
|
|
240
303
|
}
|
|
304
|
+
state.isFinalResult = (i === lastResultLineIdx) && (exitInfo !== null);
|
|
241
305
|
fragments.push(_renderJsonObj(obj, state));
|
|
242
306
|
continue;
|
|
243
307
|
} catch (e) { /* fall through */ }
|
|
@@ -255,6 +319,14 @@ function renderAgentOutput(text) {
|
|
|
255
319
|
}
|
|
256
320
|
|
|
257
321
|
flushCopilotPending();
|
|
322
|
+
|
|
323
|
+
// Fallback error banner: process exited with non-zero code but never emitted
|
|
324
|
+
// a `"type":"result"` line (CLI crashed before producing one). Without this,
|
|
325
|
+
// the user would see only stderr noise with no terminal-state indicator.
|
|
326
|
+
if (exitInfo && !exitInfo.success && lastResultLineIdx === -1) {
|
|
327
|
+
fragments.push('<div style="background:rgba(248,81,73,0.1);border:1px solid var(--red);padding:8px 12px;border-radius:8px;margin:8px 0;font-size:12px;color:var(--red)">✗ Task ended with error</div>');
|
|
328
|
+
}
|
|
329
|
+
|
|
258
330
|
return fragments.join('');
|
|
259
331
|
}
|
|
260
332
|
|
package/dashboard/js/settings.js
CHANGED
|
@@ -179,7 +179,12 @@ async function openSettings() {
|
|
|
179
179
|
'</div>' +
|
|
180
180
|
'<div>' +
|
|
181
181
|
'<label style="font-size:10px;color:var(--muted);display:block;margin-bottom:2px">CC Model</label>' +
|
|
182
|
-
|
|
182
|
+
// Wrap the input in a dedicated container so loadModelsForRuntime
|
|
183
|
+
// (which does `wrap.innerHTML = …` on the input's parent) only
|
|
184
|
+
// swaps THIS element. Without the wrap it would wipe the label
|
|
185
|
+
// and the hint below, breaking vertical alignment with the
|
|
186
|
+
// sibling CC CLI / Effort columns.
|
|
187
|
+
'<div id="set-ccModel-wrap"><input id="set-ccModel" value="' + escHtml(e.ccModel || '') + '" placeholder="(inherits Default Model)" style="width:100%;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:12px"></div>' +
|
|
183
188
|
'<div style="font-size:9px;color:var(--muted);margin-top:1px">Empty = inherit Default Model</div>' +
|
|
184
189
|
'</div>' +
|
|
185
190
|
'<div>' +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1607",
|
|
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"
|