@yemi33/minions 0.1.1602 → 0.1.1604
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-agents.js +1 -1
- package/engine/llm.js +85 -3
- package/engine/runtimes/copilot.js +15 -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.1604 (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
|
+
- preserve copilot cc action blocks
|
|
11
|
+
- improve copilot command center streaming
|
|
10
12
|
- guard runtime model races
|
|
11
13
|
- runtime-aware model picker + cross-runtime validation
|
|
12
14
|
- keep cc stream final text complete
|
|
@@ -22,7 +22,7 @@ const RUNTIME_TAGS = {
|
|
|
22
22
|
copilot: {
|
|
23
23
|
label: 'Copilot',
|
|
24
24
|
color: '#8957e5',
|
|
25
|
-
svg: '<svg viewBox="0 0 24 18" width="
|
|
25
|
+
svg: '<svg viewBox="0 0 24 18" width="13" height="13" aria-hidden="true" focusable="false" style="display:inline-block;vertical-align:-2px" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"><path d="M12 1 C18 1 21 4 21 8 V10 C22.5 10.5 23.5 12 23.5 14 C23.5 15.5 22.5 16.5 21 16.7 V17 C21 17.6 20.5 18 19.5 18 H4.5 C3.5 18 3 17.6 3 17 V16.7 C1.5 16.5 0.5 15.5 0.5 14 C0.5 12 1.5 10.5 3 10 V8 C3 4 6 1 12 1 Z"/><rect x="3.6" y="3.4" width="7.4" height="6.2" rx="2.7"/><rect x="13" y="3.4" width="7.4" height="6.2" rx="2.7"/><rect x="11" y="5.6" width="2" height="1.6" rx="0.4"/><path d="M7.4 9.6 H16.6 V15.4 C16.6 16.4 15.8 17 14.7 17 H9.3 C8.2 17 7.4 16.4 7.4 15.4 Z"/><rect x="9.4" y="10.7" width="1.7" height="4.7" rx="0.85" fill="currentColor" stroke="none"/><rect x="12.9" y="10.7" width="1.7" height="4.7" rx="0.85" fill="currentColor" stroke="none"/></svg>',
|
|
26
26
|
},
|
|
27
27
|
};
|
|
28
28
|
function _runtimeTagHtml(runtime) {
|
package/engine/llm.js
CHANGED
|
@@ -22,6 +22,7 @@ const { resolveRuntime } = require('./runtimes');
|
|
|
22
22
|
|
|
23
23
|
const MINIONS_DIR = shared.MINIONS_DIR;
|
|
24
24
|
const ENGINE_DIR = path.join(MINIONS_DIR, 'engine');
|
|
25
|
+
const COPILOT_TASK_COMPLETE_GRACE_MS = 3000;
|
|
25
26
|
|
|
26
27
|
// ─── Engine-Usage Metrics ────────────────────────────────────────────────────
|
|
27
28
|
|
|
@@ -259,6 +260,7 @@ function _createStreamAccumulator({
|
|
|
259
260
|
maxTextLength = 0,
|
|
260
261
|
onChunk = null,
|
|
261
262
|
onToolUse = null,
|
|
263
|
+
onTaskComplete = null,
|
|
262
264
|
}) {
|
|
263
265
|
let stdout = '';
|
|
264
266
|
let stderr = '';
|
|
@@ -274,6 +276,8 @@ function _createStreamAccumulator({
|
|
|
274
276
|
// narration ("I'll inspect...") that is only progress text, so terminal text
|
|
275
277
|
// comes from non-tool assistant messages or trailing deltas.
|
|
276
278
|
let copilotMessageBuffer = '';
|
|
279
|
+
let copilotTaskCompleteSeen = false;
|
|
280
|
+
let copilotTaskCompleteSummary = '';
|
|
277
281
|
|
|
278
282
|
function _streamText(value) {
|
|
279
283
|
return maxTextLength ? value.slice(-maxTextLength) : value;
|
|
@@ -284,6 +288,24 @@ function _createStreamAccumulator({
|
|
|
284
288
|
return Array.isArray(requests) && requests.length > 0;
|
|
285
289
|
}
|
|
286
290
|
|
|
291
|
+
function _captureCopilotTaskComplete(summary, success = true) {
|
|
292
|
+
if (typeof summary !== 'string' || !summary) return;
|
|
293
|
+
const finalSummary = _streamText(summary);
|
|
294
|
+
const alreadySeen = copilotTaskCompleteSeen && copilotTaskCompleteSummary === finalSummary;
|
|
295
|
+
copilotTaskCompleteSeen = true;
|
|
296
|
+
copilotTaskCompleteSummary = finalSummary;
|
|
297
|
+
const hadText = !!text;
|
|
298
|
+
if (!hadText) {
|
|
299
|
+
text = finalSummary;
|
|
300
|
+
if (onChunk && finalSummary !== lastTextSent) {
|
|
301
|
+
lastTextSent = finalSummary;
|
|
302
|
+
onChunk(finalSummary);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
copilotMessageBuffer = '';
|
|
306
|
+
if (!alreadySeen && onTaskComplete) onTaskComplete({ summary: finalSummary, success: success !== false });
|
|
307
|
+
}
|
|
308
|
+
|
|
287
309
|
function captureEvent(obj) {
|
|
288
310
|
if (!obj || typeof obj !== 'object') return;
|
|
289
311
|
|
|
@@ -323,7 +345,11 @@ function _createStreamAccumulator({
|
|
|
323
345
|
|
|
324
346
|
// ── Copilot shape ───────────────────────────────────────────────────────
|
|
325
347
|
if (obj.type === 'result' && typeof obj.sessionId === 'string') sessionId = obj.sessionId;
|
|
348
|
+
if (obj.type === 'session.task_complete') {
|
|
349
|
+
_captureCopilotTaskComplete(obj.data?.summary, obj.data?.success);
|
|
350
|
+
}
|
|
326
351
|
if (obj.type === 'assistant.message_delta' && typeof obj.data?.deltaContent === 'string') {
|
|
352
|
+
if (copilotTaskCompleteSeen) return;
|
|
327
353
|
copilotMessageBuffer += obj.data.deltaContent;
|
|
328
354
|
if (onChunk && copilotMessageBuffer !== lastTextSent) {
|
|
329
355
|
lastTextSent = copilotMessageBuffer;
|
|
@@ -340,6 +366,10 @@ function _createStreamAccumulator({
|
|
|
340
366
|
if (Array.isArray(obj.data.toolRequests)) {
|
|
341
367
|
for (const tr of obj.data.toolRequests) {
|
|
342
368
|
if (tr && tr.name) {
|
|
369
|
+
if (tr.name === 'task_complete') {
|
|
370
|
+
_captureCopilotTaskComplete(tr.arguments?.summary || tr.intentionSummary);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
343
373
|
const toolUse = { name: tr.name, input: tr.arguments || {} };
|
|
344
374
|
toolUses.push(toolUse);
|
|
345
375
|
if (onToolUse) onToolUse(toolUse.name, toolUse.input);
|
|
@@ -348,6 +378,10 @@ function _createStreamAccumulator({
|
|
|
348
378
|
}
|
|
349
379
|
}
|
|
350
380
|
if (obj.type === 'tool.execution_start' && obj.data?.toolName) {
|
|
381
|
+
if (obj.data.toolName === 'task_complete') {
|
|
382
|
+
_captureCopilotTaskComplete(obj.data.arguments?.summary);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
351
385
|
const toolUse = { name: obj.data.toolName, input: obj.data.arguments || {} };
|
|
352
386
|
// Dedup: assistant.message.toolRequests already adds this — only push if
|
|
353
387
|
// we haven't seen it yet (toolCallId would be the unique key, but we
|
|
@@ -381,9 +415,10 @@ function _createStreamAccumulator({
|
|
|
381
415
|
const ev = runtime.parseStreamChunk(trimmed);
|
|
382
416
|
if (ev) captureEvent(ev);
|
|
383
417
|
}
|
|
384
|
-
if (copilotMessageBuffer) {
|
|
418
|
+
if (copilotMessageBuffer && !copilotTaskCompleteSeen) {
|
|
385
419
|
text = _streamText(copilotMessageBuffer);
|
|
386
420
|
}
|
|
421
|
+
if (!text && copilotTaskCompleteSummary) text = copilotTaskCompleteSummary;
|
|
387
422
|
// Reconciliation: if any field is still missing, ask the runtime adapter
|
|
388
423
|
// to re-parse the whole stdout. parseOutput() may catch a result event
|
|
389
424
|
// that was malformed when streamed in chunks.
|
|
@@ -419,6 +454,18 @@ function _resolveModelFor(callOpts) {
|
|
|
419
454
|
return undefined;
|
|
420
455
|
}
|
|
421
456
|
|
|
457
|
+
function _resolveRuntimeFeatureOpts({
|
|
458
|
+
stream, disableBuiltinMcps, suppressAgentsMd, reasoningSummaries, engineConfig,
|
|
459
|
+
} = {}) {
|
|
460
|
+
const engine = engineConfig || {};
|
|
461
|
+
return {
|
|
462
|
+
stream: stream ?? engine.copilotStreamMode,
|
|
463
|
+
disableBuiltinMcps: disableBuiltinMcps ?? engine.copilotDisableBuiltinMcps,
|
|
464
|
+
suppressAgentsMd: suppressAgentsMd ?? engine.copilotSuppressAgentsMd,
|
|
465
|
+
reasoningSummaries: reasoningSummaries ?? engine.copilotReasoningSummaries,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
422
469
|
// ─── Core LLM Call ───────────────────────────────────────────────────────────
|
|
423
470
|
|
|
424
471
|
function callLLM(promptText, sysPromptText, opts = {}) {
|
|
@@ -436,6 +483,9 @@ function callLLM(promptText, sysPromptText, opts = {}) {
|
|
|
436
483
|
|
|
437
484
|
const runtime = _resolveRuntimeFor({ cli: cliOverride, engineConfig });
|
|
438
485
|
const model = _resolveModelFor({ model: modelOverride, engineConfig });
|
|
486
|
+
const runtimeFeatureOpts = _resolveRuntimeFeatureOpts({
|
|
487
|
+
stream, disableBuiltinMcps, suppressAgentsMd, reasoningSummaries, engineConfig,
|
|
488
|
+
});
|
|
439
489
|
|
|
440
490
|
let _abort = null;
|
|
441
491
|
const promise = new Promise((resolve) => {
|
|
@@ -443,13 +493,25 @@ function callLLM(promptText, sysPromptText, opts = {}) {
|
|
|
443
493
|
const { proc, cleanupFiles } = _spawnProcess(promptText, sysPromptText, {
|
|
444
494
|
direct, label, runtime, model, maxTurns, allowedTools, effort, sessionId,
|
|
445
495
|
maxBudget, bare, fallbackModel,
|
|
446
|
-
|
|
496
|
+
...runtimeFeatureOpts,
|
|
447
497
|
});
|
|
498
|
+
let taskCompleteTimer = null;
|
|
499
|
+
const scheduleTaskCompleteClose = () => {
|
|
500
|
+
if (taskCompleteTimer) return;
|
|
501
|
+
taskCompleteTimer = setTimeout(() => { try { shared.killImmediate(proc); } catch {} }, COPILOT_TASK_COMPLETE_GRACE_MS);
|
|
502
|
+
};
|
|
503
|
+
const clearTaskCompleteTimer = () => {
|
|
504
|
+
if (taskCompleteTimer) {
|
|
505
|
+
clearTimeout(taskCompleteTimer);
|
|
506
|
+
taskCompleteTimer = null;
|
|
507
|
+
}
|
|
508
|
+
};
|
|
448
509
|
const acc = _createStreamAccumulator({
|
|
449
510
|
runtime,
|
|
450
511
|
maxRawBytes: ENGINE_DEFAULTS.maxLlmRawBytes,
|
|
451
512
|
maxStderrBytes: ENGINE_DEFAULTS.maxLlmStderrBytes,
|
|
452
513
|
maxLineBufferBytes: ENGINE_DEFAULTS.maxLlmLineBufferBytes,
|
|
514
|
+
onTaskComplete: scheduleTaskCompleteClose,
|
|
453
515
|
});
|
|
454
516
|
|
|
455
517
|
_abort = () => { shared.killImmediate(proc); };
|
|
@@ -461,6 +523,7 @@ function callLLM(promptText, sysPromptText, opts = {}) {
|
|
|
461
523
|
|
|
462
524
|
proc.on('close', (code) => {
|
|
463
525
|
clearTimeout(timer);
|
|
526
|
+
clearTaskCompleteTimer();
|
|
464
527
|
for (const f of cleanupFiles) safeUnlink(f);
|
|
465
528
|
const parsed = acc.finalize();
|
|
466
529
|
const durationMs = Date.now() - _startMs;
|
|
@@ -486,6 +549,7 @@ function callLLM(promptText, sysPromptText, opts = {}) {
|
|
|
486
549
|
|
|
487
550
|
proc.on('error', (err) => {
|
|
488
551
|
clearTimeout(timer);
|
|
552
|
+
clearTaskCompleteTimer();
|
|
489
553
|
for (const f of cleanupFiles) safeUnlink(f);
|
|
490
554
|
shared.log('error', `LLM spawn error (${label}): ${err.message}`);
|
|
491
555
|
resolve({
|
|
@@ -516,6 +580,9 @@ function callLLMStreaming(promptText, sysPromptText, opts = {}) {
|
|
|
516
580
|
|
|
517
581
|
const runtime = _resolveRuntimeFor({ cli: cliOverride, engineConfig });
|
|
518
582
|
const model = _resolveModelFor({ model: modelOverride, engineConfig });
|
|
583
|
+
const runtimeFeatureOpts = _resolveRuntimeFeatureOpts({
|
|
584
|
+
stream, disableBuiltinMcps, suppressAgentsMd, reasoningSummaries, engineConfig,
|
|
585
|
+
});
|
|
519
586
|
|
|
520
587
|
let _abort = null;
|
|
521
588
|
const promise = new Promise((resolve) => {
|
|
@@ -523,8 +590,19 @@ function callLLMStreaming(promptText, sysPromptText, opts = {}) {
|
|
|
523
590
|
const { proc, cleanupFiles } = _spawnProcess(promptText, sysPromptText, {
|
|
524
591
|
direct, label, runtime, model, maxTurns, allowedTools, effort, sessionId,
|
|
525
592
|
maxBudget, bare, fallbackModel,
|
|
526
|
-
|
|
593
|
+
...runtimeFeatureOpts,
|
|
527
594
|
});
|
|
595
|
+
let taskCompleteTimer = null;
|
|
596
|
+
const scheduleTaskCompleteClose = () => {
|
|
597
|
+
if (taskCompleteTimer) return;
|
|
598
|
+
taskCompleteTimer = setTimeout(() => { try { shared.killImmediate(proc); } catch {} }, COPILOT_TASK_COMPLETE_GRACE_MS);
|
|
599
|
+
};
|
|
600
|
+
const clearTaskCompleteTimer = () => {
|
|
601
|
+
if (taskCompleteTimer) {
|
|
602
|
+
clearTimeout(taskCompleteTimer);
|
|
603
|
+
taskCompleteTimer = null;
|
|
604
|
+
}
|
|
605
|
+
};
|
|
528
606
|
const acc = _createStreamAccumulator({
|
|
529
607
|
runtime,
|
|
530
608
|
maxRawBytes: ENGINE_DEFAULTS.maxLlmRawBytes,
|
|
@@ -532,6 +610,7 @@ function callLLMStreaming(promptText, sysPromptText, opts = {}) {
|
|
|
532
610
|
maxLineBufferBytes: ENGINE_DEFAULTS.maxLlmLineBufferBytes,
|
|
533
611
|
onChunk,
|
|
534
612
|
onToolUse,
|
|
613
|
+
onTaskComplete: scheduleTaskCompleteClose,
|
|
535
614
|
});
|
|
536
615
|
|
|
537
616
|
_abort = () => { shared.killImmediate(proc); };
|
|
@@ -543,6 +622,7 @@ function callLLMStreaming(promptText, sysPromptText, opts = {}) {
|
|
|
543
622
|
|
|
544
623
|
proc.on('close', (code) => {
|
|
545
624
|
clearTimeout(timer);
|
|
625
|
+
clearTaskCompleteTimer();
|
|
546
626
|
for (const f of cleanupFiles) safeUnlink(f);
|
|
547
627
|
const parsed = acc.finalize();
|
|
548
628
|
const durationMs = Date.now() - _startMs;
|
|
@@ -565,6 +645,7 @@ function callLLMStreaming(promptText, sysPromptText, opts = {}) {
|
|
|
565
645
|
|
|
566
646
|
proc.on('error', (err) => {
|
|
567
647
|
clearTimeout(timer);
|
|
648
|
+
clearTaskCompleteTimer();
|
|
568
649
|
for (const f of cleanupFiles) safeUnlink(f);
|
|
569
650
|
shared.log('error', `LLM-stream spawn error (${label}): ${err.message}`);
|
|
570
651
|
resolve({
|
|
@@ -588,4 +669,5 @@ module.exports = {
|
|
|
588
669
|
_resetBinCache,
|
|
589
670
|
_resolveRuntimeFor,
|
|
590
671
|
_resolveModelFor,
|
|
672
|
+
_resolveRuntimeFeatureOpts,
|
|
591
673
|
};
|
|
@@ -325,6 +325,7 @@ function parseOutput(raw, { maxTextLength = 0 } = {}) {
|
|
|
325
325
|
let outputTokensTotal = 0;
|
|
326
326
|
let turnEndCount = 0;
|
|
327
327
|
let pendingDeltaContent = '';
|
|
328
|
+
let taskCompleteSummary = '';
|
|
328
329
|
|
|
329
330
|
for (const rawLine of safeRaw.split('\n')) {
|
|
330
331
|
const line = rawLine.trim();
|
|
@@ -336,17 +337,29 @@ function parseOutput(raw, { maxTextLength = 0 } = {}) {
|
|
|
336
337
|
const type = obj.type;
|
|
337
338
|
if (type === 'assistant.message_delta') {
|
|
338
339
|
const delta = obj.data?.deltaContent;
|
|
339
|
-
if (typeof delta === 'string') pendingDeltaContent += delta;
|
|
340
|
+
if (typeof delta === 'string' && !taskCompleteSummary) pendingDeltaContent += delta;
|
|
340
341
|
} else if (type === 'assistant.message') {
|
|
341
342
|
const content = obj.data?.content;
|
|
342
343
|
const toolRequests = obj.data?.toolRequests;
|
|
343
344
|
const hasToolRequests = Array.isArray(toolRequests) && toolRequests.length > 0;
|
|
345
|
+
if (hasToolRequests) {
|
|
346
|
+
for (const tr of toolRequests) {
|
|
347
|
+
const summary = tr?.name === 'task_complete' ? tr.arguments?.summary || tr.intentionSummary : '';
|
|
348
|
+
if (typeof summary === 'string' && summary) taskCompleteSummary = summary;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
344
351
|
if (typeof content === 'string') {
|
|
345
352
|
if (content && !hasToolRequests) messageContents.push(content);
|
|
346
353
|
pendingDeltaContent = '';
|
|
347
354
|
}
|
|
348
355
|
const ot = obj.data?.outputTokens;
|
|
349
356
|
if (typeof ot === 'number') outputTokensTotal += ot;
|
|
357
|
+
} else if (type === 'tool.execution_start') {
|
|
358
|
+
const summary = obj.data?.toolName === 'task_complete' ? obj.data?.arguments?.summary : '';
|
|
359
|
+
if (typeof summary === 'string' && summary) taskCompleteSummary = summary;
|
|
360
|
+
} else if (type === 'session.task_complete') {
|
|
361
|
+
const summary = obj.data?.summary;
|
|
362
|
+
if (typeof summary === 'string' && summary) taskCompleteSummary = summary;
|
|
350
363
|
} else if (type === 'assistant.turn_end') {
|
|
351
364
|
turnEndCount += 1;
|
|
352
365
|
} else if (type === 'session.tools_updated' && model == null) {
|
|
@@ -378,6 +391,7 @@ function parseOutput(raw, { maxTextLength = 0 } = {}) {
|
|
|
378
391
|
}
|
|
379
392
|
|
|
380
393
|
let text = messageContents.join('') + pendingDeltaContent;
|
|
394
|
+
if (!text && taskCompleteSummary) text = taskCompleteSummary;
|
|
381
395
|
if (maxTextLength && text.length > maxTextLength) {
|
|
382
396
|
text = text.slice(-maxTextLength);
|
|
383
397
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1604",
|
|
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"
|