@yemi33/minions 0.1.2385 → 0.1.2387
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.js +24 -20
- package/docs/command-center.md +1 -1
- package/engine/llm.js +12 -6
- package/minions.js +5 -3
- package/package.json +1 -1
package/dashboard.js
CHANGED
|
@@ -3570,18 +3570,23 @@ function _getCcLiveStream(tabId) {
|
|
|
3570
3570
|
function _touchCcLiveStream(state) {
|
|
3571
3571
|
if (!state) return;
|
|
3572
3572
|
state.updatedAt = Date.now();
|
|
3573
|
-
// W-mpmwxni2000c25c7-b — every CC streaming progress event
|
|
3574
|
-
//
|
|
3575
|
-
//
|
|
3576
|
-
//
|
|
3577
|
-
//
|
|
3578
|
-
// watchdog
|
|
3579
|
-
// it in `finally`, so late progress callbacks delivered after resolution
|
|
3580
|
-
// are a no-op.
|
|
3573
|
+
// W-mpmwxni2000c25c7-b — every CC streaming progress event routes through
|
|
3574
|
+
// here for the stall detector. The direct path also reports startup and
|
|
3575
|
+
// reasoning events through callLLMStreaming.onProgress, while the pool path
|
|
3576
|
+
// reports chunks and tools. Piggy-back the per-turn watchdog's bumpTimer on
|
|
3577
|
+
// the same heartbeat so active work cannot be killed by stale inactivity.
|
|
3578
|
+
// The watchdog removes `_bumpTimer` in `finally`, so late callbacks are no-op.
|
|
3581
3579
|
if (typeof state._bumpTimer === 'function') {
|
|
3582
3580
|
try { state._bumpTimer(); } catch { /* swallow */ }
|
|
3583
3581
|
}
|
|
3584
3582
|
}
|
|
3583
|
+
function _resolveCcDirectStreamSessionId({ sessionId, hasImages, useWorkerPool }) {
|
|
3584
|
+
// ACP session IDs belong to the live worker process. A one-off image CLI
|
|
3585
|
+
// cannot resume that session while the worker owns it, so rely on the
|
|
3586
|
+
// transcript carryover already included in the direct prompt.
|
|
3587
|
+
if (hasImages && useWorkerPool) return null;
|
|
3588
|
+
return sessionId || null;
|
|
3589
|
+
}
|
|
3585
3590
|
function _clearCcLiveTimers(tabId) {
|
|
3586
3591
|
const state = _getCcLiveStream(tabId);
|
|
3587
3592
|
if (!state) return;
|
|
@@ -9804,9 +9809,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
9804
9809
|
// already-working --attachment path below, while image-less turns keep
|
|
9805
9810
|
// using the fast warm pool.
|
|
9806
9811
|
const hasImages = Array.isArray(images) && images.length > 0;
|
|
9807
|
-
|
|
9812
|
+
const useWorkerPool = shared.resolveCcUseWorkerPool(engineConfig);
|
|
9813
|
+
if (!hasImages && useWorkerPool) {
|
|
9808
9814
|
return _invokeCcStreamViaPool({ prompt, liveState, toolUses, model, effort, engineConfig, systemPrompt, tabId });
|
|
9809
9815
|
}
|
|
9816
|
+
const directSessionId = _resolveCcDirectStreamSessionId({ sessionId, hasImages, useWorkerPool });
|
|
9810
9817
|
const setStatus = (message) => {
|
|
9811
9818
|
liveState.statusMessage = message;
|
|
9812
9819
|
if (liveState.writer) liveState.writer({ type: 'status', message });
|
|
@@ -9819,10 +9826,10 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
9819
9826
|
return callLLMStreaming(prompt, systemPrompt, {
|
|
9820
9827
|
timeout: CC_CALL_TIMEOUT_MS, label: 'command-center', model, maxTurns,
|
|
9821
9828
|
allowedTools: 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch',
|
|
9822
|
-
sessionId, effort, direct: true,
|
|
9829
|
+
sessionId: directSessionId, effort, direct: true,
|
|
9823
9830
|
engineConfig, images,
|
|
9831
|
+
onProgress: () => _touchCcLiveStream(liveState),
|
|
9824
9832
|
onChunk: (text, segmentId) => {
|
|
9825
|
-
_touchCcLiveStream(liveState);
|
|
9826
9833
|
if (liveState.statusMessage) {
|
|
9827
9834
|
setStatus('');
|
|
9828
9835
|
}
|
|
@@ -9830,7 +9837,6 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
9830
9837
|
if (liveState.writer) liveState.writer({ type: 'chunk', text, segmentId });
|
|
9831
9838
|
},
|
|
9832
9839
|
onToolUse: (name, input, id) => {
|
|
9833
|
-
_touchCcLiveStream(liveState);
|
|
9834
9840
|
if (liveState.statusMessage) {
|
|
9835
9841
|
setStatus('');
|
|
9836
9842
|
}
|
|
@@ -9842,7 +9848,6 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
9842
9848
|
if (liveState.writer) liveState.writer({ type: 'tool', name, input: _lightToolInput(input), id: id || null });
|
|
9843
9849
|
},
|
|
9844
9850
|
onToolUpdate: (id, status) => {
|
|
9845
|
-
_touchCcLiveStream(liveState);
|
|
9846
9851
|
// toolUses and liveState.tools share entry references — patch once so a
|
|
9847
9852
|
// reconnect replays the resolved (green/red) state, not just pending.
|
|
9848
9853
|
const entry = toolUses.find((e) => e && e.id === id);
|
|
@@ -10476,13 +10481,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
10476
10481
|
timeoutMs: turnTimeoutMs, label: 'command-center-stream',
|
|
10477
10482
|
}, async (registerAbort, bumpTimer) => {
|
|
10478
10483
|
// W-mpmwxni2000c25c7-b — no-progress semantics: install bumpTimer
|
|
10479
|
-
// on liveState so _touchCcLiveStream
|
|
10480
|
-
//
|
|
10481
|
-
//
|
|
10482
|
-
//
|
|
10483
|
-
//
|
|
10484
|
-
// `finally` so a late progress event after resolution can't
|
|
10485
|
-
// re-arm against a stale abort target.
|
|
10484
|
+
// on liveState so _touchCcLiveStream resets the watchdog on every
|
|
10485
|
+
// liveness signal. Direct calls include runtime startup/reasoning
|
|
10486
|
+
// events; pool calls include chunks and tool updates. A productive
|
|
10487
|
+
// turn survives up to the outer CC_CALL_TIMEOUT_MS ceiling. Cleared
|
|
10488
|
+
// in `finally` so late progress cannot re-arm a stale abort target.
|
|
10486
10489
|
liveState._bumpTimer = bumpTimer;
|
|
10487
10490
|
try {
|
|
10488
10491
|
const llmPromise = _invokeCcStream({
|
|
@@ -14814,6 +14817,7 @@ module.exports = {
|
|
|
14814
14817
|
_ccRuntimeNeedsResumeCarryover,
|
|
14815
14818
|
_ccRuntimeNeedsResumeBookkeepingGuard,
|
|
14816
14819
|
_joinCcPromptParts,
|
|
14820
|
+
_resolveCcDirectStreamSessionId, // exported for testing
|
|
14817
14821
|
_captureApiRoutesMeta,
|
|
14818
14822
|
_resetPublicApiRoutesCacheForTest,
|
|
14819
14823
|
_formatCcApiRoutesIndex,
|
package/docs/command-center.md
CHANGED
|
@@ -70,7 +70,7 @@ Any violation rejects the **whole turn** with a typed error envelope (`code: 'in
|
|
|
70
70
|
|
|
71
71
|
**Worker-pool interaction (W-mray463s001zcee2).** The opt-in Copilot ACP worker pool (`engine.ccUseWorkerPool: true`, default ON for Copilot CC) is a **separate protocol path** from the table above — `engine/cc-worker-pool.js`'s `session/prompt` call only ever sends a single text content block (`[{ type: 'text', text: prompt }]`) and has no image/attachment content-block support today. This used to mean image turns were silently dropped whenever the pool was active, with a comment incorrectly blaming a Copilot capability gap (`imageInput:false`) that doesn't exist — Copilot's `imageInput` is `true` and fully supports images on the direct spawn path. The fix: `_invokeCcStream` (dashboard.js) now checks for a non-empty `images` array **before** routing through `resolveCcUseWorkerPool` — image-bearing turns bypass the warm pool for that turn only and cold-spawn a one-off CLI process via the working `--attachment` path (same as the non-pooled path always used), while image-less turns keep using the fast warm pool. If `engine/cc-worker-pool.js`'s ACP session protocol is later extended to carry attachments (Option A), this per-turn bypass in `_invokeCcStream` should be revisited/removed.
|
|
72
72
|
|
|
73
|
-
Because that bypass can spend tens of seconds starting a one-off process before the first model token, the SSE stream shows a user-focused “Looking at the image…” status instead of exposing the cold-start implementation detail or leaving an apparently stalled spinner.
|
|
73
|
+
Because that bypass can spend tens of seconds starting a one-off process before the first model token, the SSE stream shows a user-focused “Looking at the image…” status instead of exposing the cold-start implementation detail or leaving an apparently stalled spinner. The one-off process does not resume the live worker's process-scoped ACP session ID: it starts a transport-compatible fresh session and receives prior conversation context through the existing transcript carryover. Every parsed direct-runtime event, including startup and reasoning events, resets the five-minute silence watchdog; the safety bound therefore still catches a truly silent process without killing an image turn that is making progress.
|
|
74
74
|
|
|
75
75
|
`_resolveImageOpts` (in `engine/llm.js`) is pure and unit-tested: it forwards the `images` list only when `runtime.capabilities.imageInput` is truthy, otherwise returns the typed error so CC/doc-chat surface the envelope instead of a silently-text-only reply. `_spawnProcess` re-applies the same gate (`if (!caps.imageInput) adapterOpts.images = undefined`) as defense in depth. Image **filenames** are run through the untrusted-input fence (`buildSource('cc-image-filename', …)`) before they reach prompt text, since they are user-supplied.
|
|
76
76
|
|
package/engine/llm.js
CHANGED
|
@@ -482,6 +482,7 @@ function _createStreamAccumulator({
|
|
|
482
482
|
onTaskComplete = null,
|
|
483
483
|
onTerminalResult = null,
|
|
484
484
|
onThinking = null,
|
|
485
|
+
onProgress = null,
|
|
485
486
|
}) {
|
|
486
487
|
if (!runtime?.capabilities?.streamConsumer || typeof runtime.createStreamConsumer !== 'function') {
|
|
487
488
|
throw new Error(`runtime ${runtime?.name || '<unknown>'} missing createStreamConsumer (capabilities.streamConsumer)`);
|
|
@@ -600,6 +601,11 @@ function _createStreamAccumulator({
|
|
|
600
601
|
};
|
|
601
602
|
|
|
602
603
|
const consumer = runtime.createStreamConsumer(ctx);
|
|
604
|
+
function consumeEvent(ev) {
|
|
605
|
+
if (!ev) return;
|
|
606
|
+
if (onProgress) onProgress(ev);
|
|
607
|
+
consumer.consume(ev);
|
|
608
|
+
}
|
|
603
609
|
|
|
604
610
|
function ingestStdout(chunk) {
|
|
605
611
|
const str = chunk == null ? '' : chunk.toString();
|
|
@@ -608,8 +614,7 @@ function _createStreamAccumulator({
|
|
|
608
614
|
const lines = lineBuf.split('\n');
|
|
609
615
|
lineBuf = lines.pop() || '';
|
|
610
616
|
for (const line of lines) {
|
|
611
|
-
|
|
612
|
-
if (ev) consumer.consume(ev);
|
|
617
|
+
consumeEvent(runtime.parseStreamChunk(line));
|
|
613
618
|
}
|
|
614
619
|
}
|
|
615
620
|
|
|
@@ -619,10 +624,7 @@ function _createStreamAccumulator({
|
|
|
619
624
|
|
|
620
625
|
function finalize() {
|
|
621
626
|
const trimmed = lineBuf.trim();
|
|
622
|
-
if (trimmed)
|
|
623
|
-
const ev = runtime.parseStreamChunk(trimmed);
|
|
624
|
-
if (ev) consumer.consume(ev);
|
|
625
|
-
}
|
|
627
|
+
if (trimmed) consumeEvent(runtime.parseStreamChunk(trimmed));
|
|
626
628
|
if (!text && lastTaskCompleteSummary) text = lastTaskCompleteSummary;
|
|
627
629
|
// Reconciliation: always let the runtime adapter re-parse the complete
|
|
628
630
|
// stdout. Stream consumers emit live progress, but parseOutput() owns the
|
|
@@ -851,11 +853,14 @@ function callLLM(promptText, sysPromptText, opts = {}) {
|
|
|
851
853
|
* onChunk(text, segmentIndex) is called for each assistant text block as it
|
|
852
854
|
* arrives; segmentIndex increments across distinct text blocks so consumers can
|
|
853
855
|
* keep them visually separate instead of welding them together.
|
|
856
|
+
* onProgress(event) is called for every parsed runtime event, including startup
|
|
857
|
+
* and reasoning events that do not produce a text or tool callback.
|
|
854
858
|
*/
|
|
855
859
|
function callLLMStreaming(promptText, sysPromptText, opts = {}) {
|
|
856
860
|
const {
|
|
857
861
|
timeout = 120000, label = 'llm', maxTurns = 1, allowedTools = '',
|
|
858
862
|
sessionId = null, onChunk = () => {}, onToolUse = null, onToolUpdate = null,
|
|
863
|
+
onProgress = null,
|
|
859
864
|
effort = null, direct = false,
|
|
860
865
|
model: modelOverride, cli: cliOverride, engineConfig,
|
|
861
866
|
maxBudget, bare, fallbackModel,
|
|
@@ -903,6 +908,7 @@ function callLLMStreaming(promptText, sysPromptText, opts = {}) {
|
|
|
903
908
|
// 'exit'/'close' when an inherited pipe keeps the parent's FDs open.
|
|
904
909
|
onTerminalResult: () => scheduleExitFallback(exitCode != null ? exitCode : 0),
|
|
905
910
|
onThinking: opts.onThinking || null,
|
|
911
|
+
onProgress,
|
|
906
912
|
});
|
|
907
913
|
|
|
908
914
|
_abort = () => { shared.killImmediate(proc); };
|
package/minions.js
CHANGED
|
@@ -423,11 +423,13 @@ async function initMinions({ skipScan = false, scanRoot, scanDepth } = {}) {
|
|
|
423
423
|
config.engine.defaultCli = detected[0];
|
|
424
424
|
console.log(`\n ✓ Detected ${detected[0]} CLI — set as fleet default runtime`);
|
|
425
425
|
} else if (detected.length > 1) {
|
|
426
|
-
//
|
|
427
|
-
config.engine.defaultCli = detected.includes(
|
|
426
|
+
// Prefer the canonical fleet default when it is installed.
|
|
427
|
+
config.engine.defaultCli = detected.includes(ENGINE_DEFAULTS.defaultCli)
|
|
428
|
+
? ENGINE_DEFAULTS.defaultCli
|
|
429
|
+
: detected[0];
|
|
428
430
|
console.log(`\n ✓ Detected ${detected.join(' + ')} — fleet default set to ${config.engine.defaultCli}`);
|
|
429
431
|
}
|
|
430
|
-
// If nothing detected, leave defaultCli unset
|
|
432
|
+
// If nothing is detected, leave defaultCli unset so the engine uses its canonical default.
|
|
431
433
|
}
|
|
432
434
|
saveConfig(config);
|
|
433
435
|
console.log(`\n Minions initialized at ${MINIONS_HOME}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2387",
|
|
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"
|