@yemi33/minions 0.1.1936 → 0.1.1937
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/command-center.js +12 -15
- package/dashboard/js/render-utils.js +20 -0
- package/dashboard.js +33 -22
- package/engine/cc-worker-pool.js +54 -25
- package/package.json +1 -1
|
@@ -397,13 +397,7 @@ function ccSwitchTab(id) {
|
|
|
397
397
|
var html = '';
|
|
398
398
|
var tools = tab._toolsUsed || [];
|
|
399
399
|
if (tools.length > 0) {
|
|
400
|
-
html += '<div style="margin-bottom:6px">';
|
|
401
|
-
tools.forEach(function(t) {
|
|
402
|
-
var name = typeof t === 'string' ? t : t.name;
|
|
403
|
-
var input = typeof t === 'string' ? {} : (t.input || {});
|
|
404
|
-
html += '<div style="color:var(--muted);font-size:10px;font-family:monospace"><span style="flex-shrink:0">●</span> ' + formatToolSummary(name, input) + '</div>';
|
|
405
|
-
});
|
|
406
|
-
html += '</div>';
|
|
400
|
+
html += '<div style="margin-bottom:6px">' + tools.map(renderToolChip).join('') + '</div>';
|
|
407
401
|
}
|
|
408
402
|
var text = tab._streamedText || '';
|
|
409
403
|
if (text) html += renderMd(text);
|
|
@@ -779,13 +773,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
|
|
|
779
773
|
}
|
|
780
774
|
var html = '';
|
|
781
775
|
if (toolsUsed.length > 0) {
|
|
782
|
-
html += '<div style="margin-bottom:6px">';
|
|
783
|
-
toolsUsed.forEach(function(t) {
|
|
784
|
-
var name = typeof t === 'string' ? t : t.name;
|
|
785
|
-
var input = typeof t === 'string' ? {} : (t.input || {});
|
|
786
|
-
html += '<div style="color:var(--muted);font-size:10px;font-family:monospace"><span style="flex-shrink:0">●</span> ' + formatToolSummary(name, input) + '</div>';
|
|
787
|
-
});
|
|
788
|
-
html += '</div>';
|
|
776
|
+
html += '<div style="margin-bottom:6px">' + toolsUsed.map(renderToolChip).join('') + '</div>';
|
|
789
777
|
}
|
|
790
778
|
if (streamedText) {
|
|
791
779
|
html += renderMd(streamedText);
|
|
@@ -851,10 +839,19 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
|
|
|
851
839
|
} else if (evt.type === 'heartbeat') {
|
|
852
840
|
return;
|
|
853
841
|
} else if (evt.type === 'tool') {
|
|
854
|
-
toolsUsed.push({ name: evt.name, input: evt.input || {} });
|
|
842
|
+
toolsUsed.push({ name: evt.name, input: evt.input || {}, id: evt.id || null, status: evt.id ? 'pending' : null });
|
|
855
843
|
if (activeTab) activeTab._toolsUsed = toolsUsed.slice();
|
|
856
844
|
updateStreamDiv();
|
|
857
845
|
if (msgs.scrollHeight - msgs.scrollTop - msgs.clientHeight < 150) msgs.scrollTop = msgs.scrollHeight;
|
|
846
|
+
} else if (evt.type === 'tool-update') {
|
|
847
|
+
for (var ti = 0; ti < toolsUsed.length; ti++) {
|
|
848
|
+
if (toolsUsed[ti] && toolsUsed[ti].id === evt.id) {
|
|
849
|
+
toolsUsed[ti].status = evt.status;
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (activeTab) activeTab._toolsUsed = toolsUsed.slice();
|
|
854
|
+
updateStreamDiv();
|
|
858
855
|
} else if (evt.type === 'done') {
|
|
859
856
|
terminalEventSeen = true;
|
|
860
857
|
_cleanupStreamDiv();
|
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
// dashboard/js/render-utils.js — Shared formatting helpers for agent output rendering
|
|
2
2
|
// Depends on: escHtml() and renderMd() from utils.js (loaded before this file)
|
|
3
3
|
|
|
4
|
+
// marker + color per tool-call status. ACP `tool_call_update` flips chips from
|
|
5
|
+
// pending → completed/failed mid-stream so the user sees real progress; the
|
|
6
|
+
// non-pool (Claude direct) path emits status=null and stays on the neutral dot.
|
|
7
|
+
var _CC_TOOL_CHIP_STYLE = {
|
|
8
|
+
pending: { marker: '●', color: 'var(--muted)' },
|
|
9
|
+
completed: { marker: '✓', color: 'var(--green, #4ade80)' },
|
|
10
|
+
failed: { marker: '✕', color: 'var(--red, #ef4444)' },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function renderToolChip(t) {
|
|
14
|
+
var name = typeof t === 'string' ? t : t.name;
|
|
15
|
+
var input = typeof t === 'string' ? {} : (t.input || {});
|
|
16
|
+
var status = typeof t === 'string' ? null : (t.status || null);
|
|
17
|
+
var style = _CC_TOOL_CHIP_STYLE[status] || _CC_TOOL_CHIP_STYLE.pending;
|
|
18
|
+
return '<div style="color:' + style.color + ';font-size:10px;font-family:monospace">'
|
|
19
|
+
+ '<span style="flex-shrink:0">' + style.marker + '</span> '
|
|
20
|
+
+ formatToolSummary(name, input)
|
|
21
|
+
+ '</div>';
|
|
22
|
+
}
|
|
23
|
+
|
|
4
24
|
/**
|
|
5
25
|
* Returns a one-line human-readable summary for a Claude tool call.
|
|
6
26
|
* @param {string} name - Tool name (e.g. 'Bash', 'Read', 'Edit')
|
package/dashboard.js
CHANGED
|
@@ -2498,7 +2498,7 @@ async function _preflightModelCheck({ runtime: cliOverride, model: modelOverride
|
|
|
2498
2498
|
* document body. Always re-sending extraContext is correctness-safe; the
|
|
2499
2499
|
* pool's warm-process saving is preserved regardless.
|
|
2500
2500
|
*/
|
|
2501
|
-
function _invokeDocChatViaPool({ prompt, model, effort, engineConfig, systemPrompt, sessionKey, freshSession, timeoutMs, onChunk, onToolUse }) {
|
|
2501
|
+
function _invokeDocChatViaPool({ prompt, model, effort, engineConfig, systemPrompt, sessionKey, freshSession, timeoutMs, onChunk, onToolUse, onToolUpdate }) {
|
|
2502
2502
|
const oneShot = !!freshSession;
|
|
2503
2503
|
const tabKey = oneShot
|
|
2504
2504
|
? 'doc-chat:fresh:' + shared.uid()
|
|
@@ -2576,9 +2576,14 @@ function _invokeDocChatViaPool({ prompt, model, effort, engineConfig, systemProm
|
|
|
2576
2576
|
try { onChunk(accumulated); } catch { /* swallow */ }
|
|
2577
2577
|
}
|
|
2578
2578
|
},
|
|
2579
|
-
onToolUse: (name, input) => {
|
|
2579
|
+
onToolUse: (name, input, toolCallId) => {
|
|
2580
2580
|
if (onToolUse) {
|
|
2581
|
-
try { onToolUse(name, input || {}); } catch { /* swallow */ }
|
|
2581
|
+
try { onToolUse(name, input || {}, toolCallId); } catch { /* swallow */ }
|
|
2582
|
+
}
|
|
2583
|
+
},
|
|
2584
|
+
onToolUpdate: (toolCallId, status) => {
|
|
2585
|
+
if (onToolUpdate) {
|
|
2586
|
+
try { onToolUpdate(toolCallId, status); } catch { /* swallow */ }
|
|
2582
2587
|
}
|
|
2583
2588
|
},
|
|
2584
2589
|
onDone: () => {
|
|
@@ -2762,7 +2767,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
2762
2767
|
return result;
|
|
2763
2768
|
}
|
|
2764
2769
|
|
|
2765
|
-
async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext, label = 'command-center', timeout = CC_CALL_TIMEOUT_MS, maxTurns, allowedTools = 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch', skipStatePreamble = false, skipPreflight = false, model, onAbortReady, onChunk, onToolUse, onRetry, systemPrompt = CC_STATIC_SYSTEM_PROMPT, transcript, turnId, freshSession = false } = {}) {
|
|
2770
|
+
async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext, label = 'command-center', timeout = CC_CALL_TIMEOUT_MS, maxTurns, allowedTools = 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch', skipStatePreamble = false, skipPreflight = false, model, onAbortReady, onChunk, onToolUse, onToolUpdate, onRetry, systemPrompt = CC_STATIC_SYSTEM_PROMPT, transcript, turnId, freshSession = false } = {}) {
|
|
2766
2771
|
if (!maxTurns) maxTurns = CONFIG.engine?.ccMaxTurns || shared.ENGINE_DEFAULTS.ccMaxTurns;
|
|
2767
2772
|
if (!model) model = CONFIG.engine?.ccModel || shared.ENGINE_DEFAULTS.ccModel;
|
|
2768
2773
|
const ccEffort = CONFIG.engine?.ccEffort || shared.ENGINE_DEFAULTS.ccEffort;
|
|
@@ -2793,7 +2798,7 @@ async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext
|
|
|
2793
2798
|
const p = _invokeDocChatViaPool({
|
|
2794
2799
|
prompt: poolPrompt, sessionKey, model, effort: ccEffort,
|
|
2795
2800
|
engineConfig: CONFIG.engine, systemPrompt,
|
|
2796
|
-
onChunk, onToolUse,
|
|
2801
|
+
onChunk, onToolUse, onToolUpdate,
|
|
2797
2802
|
freshSession, timeoutMs: timeout,
|
|
2798
2803
|
});
|
|
2799
2804
|
if (onAbortReady) onAbortReady(p.abort);
|
|
@@ -6163,17 +6168,10 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
6163
6168
|
const abort = ccInFlightAborts.get(tabId);
|
|
6164
6169
|
if (abort) { try { abort(); } catch {} }
|
|
6165
6170
|
}
|
|
6166
|
-
//
|
|
6167
|
-
//
|
|
6168
|
-
// remote daemon stops generating into a torn-down session. The pool
|
|
6169
|
-
// exposes cancellation via the SessionHandle returned from getSession;
|
|
6170
|
-
// we don't keep that handle around here, so route through closeTab to
|
|
6171
|
-
// both cancel inflight and tear down the worker (cheaper than tracking
|
|
6172
|
-
// per-tab handles in dashboard state, and matches "tab close" semantics
|
|
6173
|
-
// — if the user explicitly aborted, we don't owe them a warm process).
|
|
6174
|
-
// Off when the flag is off so legacy SIGTERM-only behavior is preserved.
|
|
6171
|
+
// Cancel the inflight ACP turn but keep the warm worker; closing the
|
|
6172
|
+
// tab here would force the next turn to pay the ~7-8 s cold-spawn.
|
|
6175
6173
|
if (shared.resolveCcUseWorkerPool(CONFIG.engine)) {
|
|
6176
|
-
try { ccWorkerPool.
|
|
6174
|
+
try { ccWorkerPool.cancelInflight(tabId); } catch { /* swallow */ }
|
|
6177
6175
|
}
|
|
6178
6176
|
_clearCcLiveStream(tabId);
|
|
6179
6177
|
_releaseCCTab(tabId);
|
|
@@ -6333,9 +6331,10 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
6333
6331
|
},
|
|
6334
6332
|
onToolUse: (name, input) => {
|
|
6335
6333
|
_touchCcLiveStream(liveState);
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6334
|
+
const entry = { name, input: input || {}, id: null, status: null };
|
|
6335
|
+
toolUses.push(entry);
|
|
6336
|
+
liveState.tools.push(entry);
|
|
6337
|
+
if (liveState.writer) liveState.writer({ type: 'tool', name, input: _lightToolInput(input), id: null });
|
|
6339
6338
|
},
|
|
6340
6339
|
});
|
|
6341
6340
|
}
|
|
@@ -6431,14 +6430,26 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
6431
6430
|
liveState.text = accumulated;
|
|
6432
6431
|
if (liveState.writer) liveState.writer({ type: 'chunk', text: accumulated });
|
|
6433
6432
|
},
|
|
6434
|
-
onToolUse: (name, input) => {
|
|
6433
|
+
onToolUse: (name, input, toolCallId) => {
|
|
6435
6434
|
if (_tFirstTool == null) _tFirstTool = Date.now();
|
|
6436
6435
|
_toolUseCount += 1;
|
|
6437
6436
|
_touchCcLiveStream(liveState);
|
|
6438
6437
|
const safeInput = input || {};
|
|
6439
|
-
|
|
6440
|
-
if (Array.isArray(
|
|
6441
|
-
if (liveState.
|
|
6438
|
+
const entry = { name, input: safeInput, id: toolCallId || null, status: toolCallId ? 'pending' : null };
|
|
6439
|
+
if (Array.isArray(toolUses)) toolUses.push(entry);
|
|
6440
|
+
if (Array.isArray(liveState.tools)) liveState.tools.push(entry);
|
|
6441
|
+
if (liveState.writer) liveState.writer({ type: 'tool', name, input: _lightToolInput(safeInput), id: toolCallId || null });
|
|
6442
|
+
},
|
|
6443
|
+
onToolUpdate: (toolCallId, status) => {
|
|
6444
|
+
_touchCcLiveStream(liveState);
|
|
6445
|
+
// toolUses and liveState.tools hold the same entry references (both
|
|
6446
|
+
// populated via onToolUse above), so mutating in one side reflects
|
|
6447
|
+
// in the other. Patch once.
|
|
6448
|
+
if (Array.isArray(toolUses)) {
|
|
6449
|
+
const entry = toolUses.find((e) => e && e.id === toolCallId);
|
|
6450
|
+
if (entry) entry.status = status;
|
|
6451
|
+
}
|
|
6452
|
+
if (liveState.writer) liveState.writer({ type: 'tool-update', id: toolCallId, status });
|
|
6442
6453
|
},
|
|
6443
6454
|
onDone: () => {
|
|
6444
6455
|
_emitTimingLog(_lifecycle, _tSessionReady, Date.now(), 'done');
|
package/engine/cc-worker-pool.js
CHANGED
|
@@ -252,17 +252,19 @@ class Worker {
|
|
|
252
252
|
try { this.inflight.onChunk(text); } catch { /* swallow */ }
|
|
253
253
|
}
|
|
254
254
|
} else if (update.sessionUpdate === 'tool_call' && this.inflight.onToolUse) {
|
|
255
|
-
// ACP `tool_call` (
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
// formatToolSummary (Bash → "$ <cmd>", Read → "Reading <path>", etc.)
|
|
259
|
-
// works unchanged. Status updates (`tool_call_update`, status:
|
|
260
|
-
// completed) carry the result and are ignored here — surfacing
|
|
261
|
-
// results too would double the chip count without adding info the
|
|
262
|
-
// user can act on.
|
|
255
|
+
// ACP `tool_call` (pending) → Claude-style {name, input} via
|
|
256
|
+
// _mapAcpToolCallToToolUse so the dashboard's formatToolSummary
|
|
257
|
+
// formatters (Bash → "$ <cmd>", etc.) work unchanged.
|
|
263
258
|
const mapped = _mapAcpToolCallToToolUse(update);
|
|
264
259
|
if (mapped) {
|
|
265
|
-
try { this.inflight.onToolUse(mapped.name, mapped.input); }
|
|
260
|
+
try { this.inflight.onToolUse(mapped.name, mapped.input, update.toolCallId); }
|
|
261
|
+
catch { /* swallow */ }
|
|
262
|
+
}
|
|
263
|
+
} else if (update.sessionUpdate === 'tool_call_update' && this.inflight.onToolUpdate) {
|
|
264
|
+
// Terminal-state updates only (completed/failed). In-progress updates
|
|
265
|
+
// exist in the ACP spec but add chip churn without actionable info.
|
|
266
|
+
if (update.status === 'completed' || update.status === 'failed') {
|
|
267
|
+
try { this.inflight.onToolUpdate(update.toolCallId, update.status); }
|
|
266
268
|
catch { /* swallow */ }
|
|
267
269
|
}
|
|
268
270
|
}
|
|
@@ -290,7 +292,7 @@ class Worker {
|
|
|
290
292
|
|
|
291
293
|
// ── Stream a single turn ───────────────────────────────────────────────
|
|
292
294
|
stream(promptText, opts = {}) {
|
|
293
|
-
const { onChunk, onToolUse, onDone, onError, signal, systemPromptText } = opts;
|
|
295
|
+
const { onChunk, onToolUse, onToolUpdate, onDone, onError, signal, systemPromptText } = opts;
|
|
294
296
|
if (this.killed) {
|
|
295
297
|
const err = new Error('cc-worker-pool: tab is closed');
|
|
296
298
|
if (onError) try { onError(err); } catch { /* swallow */ }
|
|
@@ -319,6 +321,7 @@ class Worker {
|
|
|
319
321
|
sessionId: this.sessionId,
|
|
320
322
|
onChunk,
|
|
321
323
|
onToolUse,
|
|
324
|
+
onToolUpdate,
|
|
322
325
|
onDone,
|
|
323
326
|
onError,
|
|
324
327
|
signal,
|
|
@@ -449,31 +452,44 @@ function _mapAcpToolCallToToolUse(update) {
|
|
|
449
452
|
const rawInput = (update.rawInput && typeof update.rawInput === 'object') ? update.rawInput : {};
|
|
450
453
|
const kind = String(update.kind || '').toLowerCase();
|
|
451
454
|
const title = update.title || '';
|
|
452
|
-
|
|
455
|
+
|
|
456
|
+
// Field-name normalization: Copilot ACP and Claude tool_use use different
|
|
457
|
+
// keys for the same concept (Copilot `path`, Claude `file_path`). Normalize
|
|
458
|
+
// here so the dashboard's formatToolSummary — written against Claude's
|
|
459
|
+
// names — produces the same chip text on both runtimes.
|
|
460
|
+
const filePath = rawInput.file_path || rawInput.path || rawInput.filePath || '';
|
|
461
|
+
|
|
462
|
+
// Pattern (Grep) — Copilot uses `paths` (plural) for the search scope where
|
|
463
|
+
// Claude's Grep takes `path`.
|
|
464
|
+
if (typeof rawInput.pattern === 'string') {
|
|
465
|
+
return {
|
|
466
|
+
name: 'Grep',
|
|
467
|
+
input: { pattern: rawInput.pattern, path: rawInput.paths || rawInput.path || '.' },
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
453
471
|
switch (kind) {
|
|
454
472
|
case 'execute':
|
|
455
473
|
return { name: 'Bash', input: rawInput };
|
|
456
474
|
case 'read':
|
|
457
|
-
|
|
475
|
+
// ACP overloads `read` for both file-view and grep — the pattern check
|
|
476
|
+
// above already handled grep, so this branch is the file-view case.
|
|
477
|
+
return { name: 'Read', input: { file_path: filePath } };
|
|
458
478
|
case 'edit':
|
|
459
|
-
return { name: 'Edit', input:
|
|
460
|
-
case 'search':
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
// (matches the dashboard's Grep formatter "Searching <pat> in <path>").
|
|
464
|
-
const isGrep = typeof rawInput.path === 'string' || typeof rawInput.regex === 'string';
|
|
465
|
-
return { name: isGrep ? 'Grep' : 'Glob', input: rawInput };
|
|
466
|
-
}
|
|
479
|
+
return { name: 'Edit', input: { file_path: filePath } };
|
|
480
|
+
case 'search':
|
|
481
|
+
// Pattern check above handled the grep case; arriving here means glob.
|
|
482
|
+
return { name: 'Glob', input: rawInput };
|
|
467
483
|
case 'fetch':
|
|
468
484
|
return { name: 'WebFetch', input: rawInput };
|
|
469
485
|
case 'think':
|
|
470
|
-
// No equivalent Claude tool; show the title so the user sees Copilot's
|
|
471
|
-
// own description of what it's thinking about.
|
|
472
486
|
return { name: title || 'Think', input: rawInput };
|
|
473
487
|
default:
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
|
|
488
|
+
// Unknown kind — use ACP's human-readable title as the chip label and
|
|
489
|
+
// drop rawInput so formatToolSummary's default branch shows just the
|
|
490
|
+
// title (avoids `<title>(<key>: <val>)` clutter when the input shape
|
|
491
|
+
// is unfamiliar).
|
|
492
|
+
return { name: title || kind || 'Tool', input: {} };
|
|
477
493
|
}
|
|
478
494
|
}
|
|
479
495
|
|
|
@@ -552,6 +568,18 @@ function closeTab(tabId) {
|
|
|
552
568
|
worker.close();
|
|
553
569
|
}
|
|
554
570
|
|
|
571
|
+
// Cancel the currently in-flight prompt on this tab without killing the
|
|
572
|
+
// worker. Sends ACP `session/cancel` so the remote daemon stops generating;
|
|
573
|
+
// the warm process + initialized MCP servers + session state are preserved
|
|
574
|
+
// so the next prompt skips the ~7-8 s cold-spawn cost. No-op if the tab has
|
|
575
|
+
// no worker or no inflight prompt.
|
|
576
|
+
function cancelInflight(tabId) {
|
|
577
|
+
const worker = _tabs.get(tabId);
|
|
578
|
+
if (!worker || worker.killed || !worker.inflight) return false;
|
|
579
|
+
try { worker.cancel(); } catch { /* swallow */ }
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
|
|
555
583
|
function shutdown() {
|
|
556
584
|
for (const worker of _tabs.values()) {
|
|
557
585
|
try { worker.close(); } catch { /* swallow */ }
|
|
@@ -583,6 +611,7 @@ function _reapIdleTabs() {
|
|
|
583
611
|
module.exports = {
|
|
584
612
|
getSession,
|
|
585
613
|
closeTab,
|
|
614
|
+
cancelInflight,
|
|
586
615
|
shutdown,
|
|
587
616
|
// Exposed for unit tests; engine code MUST go through the public API.
|
|
588
617
|
_internals,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1937",
|
|
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"
|