@yemi33/minions 0.1.1598 → 0.1.1600

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 CHANGED
@@ -1,17 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1598 (2026-04-28)
3
+ ## 0.1.1600 (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
+ - keep cc stream final text complete
10
11
  - switch Copilot icon to outline style for cleaner inline read
11
12
  - un-invert Copilot — purple silhouette + white cutouts
12
13
  - redraw Copilot icon to match actual mascot — vertical eye pills, not grill bars
13
14
  - invert Copilot icon colors for better dark-theme visibility
14
15
 
16
+ ### Other
17
+ - Stream CC chunks incrementally
18
+
15
19
  ## 0.1.1592 (2026-04-28)
16
20
 
17
21
  ### Features
@@ -54,6 +54,22 @@ function _ccActiveTab() {
54
54
  return _ccTabs.find(function(t) { return t.id === _ccActiveTabId; }) || null;
55
55
  }
56
56
 
57
+ function _ccMergeStreamText(prev, incoming) {
58
+ var current = prev || '';
59
+ var next = incoming || '';
60
+ if (!current) return next;
61
+ if (!next) return current;
62
+ if (next === current) return current;
63
+ if (next.startsWith(current)) return next;
64
+ if (current.startsWith(next)) return current;
65
+ for (var overlap = Math.min(current.length, next.length); overlap > 0; overlap--) {
66
+ if (current.slice(-overlap) === next.slice(0, overlap)) {
67
+ return current + next.slice(overlap);
68
+ }
69
+ }
70
+ return current + '\n\n' + next;
71
+ }
72
+
57
73
  async function _ccDashboardHealth() {
58
74
  var controller = new AbortController();
59
75
  var timer = setTimeout(function() { controller.abort(); }, 3000);
@@ -580,7 +596,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId) {
580
596
 
581
597
  async function _handleEvent(evt) {
582
598
  if (evt.type === 'chunk') {
583
- streamedText = evt.text;
599
+ streamedText = _ccMergeStreamText(streamedText, evt.text || '');
584
600
  if (activeTab) activeTab._streamedText = streamedText;
585
601
  updateStreamDiv();
586
602
  } else if (evt.type === 'heartbeat') {
@@ -35,6 +35,20 @@ function _changed(key, value) {
35
35
  return true;
36
36
  }
37
37
 
38
+ function _formatCcPowerLabel(autoMode) {
39
+ var runtime = autoMode && autoMode.ccCli ? String(autoMode.ccCli) : 'claude';
40
+ var runtimeLabel = runtime.charAt(0).toUpperCase() + runtime.slice(1);
41
+ var model = autoMode && autoMode.ccModel ? String(autoMode.ccModel) : '';
42
+ return 'Ask anything, dispatch work, manage plans — powered by ' + runtimeLabel + (model ? ' (' + model + ')' : '');
43
+ }
44
+
45
+ function _formatCcDrawerLabel(autoMode) {
46
+ var runtime = autoMode && autoMode.ccCli ? String(autoMode.ccCli) : 'claude';
47
+ var runtimeLabel = runtime.charAt(0).toUpperCase() + runtime.slice(1);
48
+ var model = autoMode && autoMode.ccModel ? String(autoMode.ccModel) : '';
49
+ return runtimeLabel + (model ? ' (' + model + ')' : '') + '-powered. Full minions context. Enter to send, Shift+Enter for newline.';
50
+ }
51
+
38
52
  function _processStatusUpdate(data) {
39
53
  // Detect fresh install — clear stale browser state if install ID changed
40
54
  if (data.installId) {
@@ -53,6 +67,10 @@ function _processStatusUpdate(data) {
53
67
  if (autoEl) autoEl.innerHTML = data.autoMode?.approvePlans
54
68
  ? '<span style="font-size:9px;font-weight:600;padding:1px 6px;border-radius:3px;background:rgba(63,185,80,0.15);color:var(--green);border:1px solid rgba(63,185,80,0.3)">AUTO-APPROVE</span>'
55
69
  : '';
70
+ const ccLabelEl = document.getElementById('cmd-powered-by');
71
+ if (ccLabelEl) ccLabelEl.textContent = _formatCcPowerLabel(data.autoMode);
72
+ const ccDrawerLabelEl = document.getElementById('cc-powered-by');
73
+ if (ccDrawerLabelEl) ccDrawerLabelEl.textContent = _formatCcDrawerLabel(data.autoMode);
56
74
  const threshEl = document.getElementById('inbox-threshold');
57
75
  if (threshEl && data.autoMode?.inboxThreshold) threshEl.textContent = data.autoMode.inboxThreshold;
58
76
 
@@ -43,7 +43,7 @@
43
43
  <textarea id="cc-input" rows="2" placeholder="Ask anything or give a command..." style="flex:1;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:12px;resize:none;font-family:inherit" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();ccSend()}"></textarea>
44
44
  <button onclick="ccSend()" style="background:var(--blue);color:#fff;border:none;border-radius:6px;padding:8px 14px;font-size:12px;font-weight:600;cursor:pointer;align-self:flex-end">Send</button>
45
45
  </div>
46
- <div style="font-size:9px;color:var(--muted);margin-top:4px">Sonnet-powered. Full minions context. Enter to send, Shift+Enter for newline.</div>
46
+ <div id="cc-powered-by" style="font-size:9px;color:var(--muted);margin-top:4px">Full minions context. Enter to send, Shift+Enter for newline.</div>
47
47
  </div>
48
48
  </div>
49
49
 
@@ -10,7 +10,7 @@
10
10
  <div class="cmd-meta" id="cmd-meta" style="display:none"></div>
11
11
  <div class="cmd-hints">
12
12
  <span style="color:var(--blue);font-weight:600">Command Center</span>
13
- <span>Ask anything, dispatch work, manage plans — powered by Sonnet</span>
13
+ <span id="cmd-powered-by">Ask anything, dispatch work, manage plans</span>
14
14
  <button class="cmd-history-btn" onclick="cmdShowHistory()">Past Commands</button>
15
15
  <button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--green);border-color:var(--green)" onclick="openQuickNoteModal()">+ Note</button>
16
16
  </div>
package/dashboard.js CHANGED
@@ -474,7 +474,8 @@ function getStatus() {
474
474
  decompose: CONFIG.engine?.autoDecompose !== false,
475
475
  tempAgents: !!CONFIG.engine?.allowTempAgents,
476
476
  inboxThreshold: CONFIG.engine?.inboxConsolidateThreshold || shared.ENGINE_DEFAULTS.inboxConsolidateThreshold,
477
- ccModel: CONFIG.engine?.ccModel || shared.ENGINE_DEFAULTS.ccModel,
477
+ ccCli: shared.resolveCcCli(CONFIG.engine),
478
+ ccModel: shared.resolveCcModel(CONFIG.engine),
478
479
  ccEffort: CONFIG.engine?.ccEffort || shared.ENGINE_DEFAULTS.ccEffort,
479
480
  },
480
481
  initialized: !!(CONFIG.agents && Object.keys(CONFIG.agents).length > 0),
package/engine/llm.js CHANGED
@@ -270,11 +270,20 @@ function _createStreamAccumulator({
270
270
  const toolUses = [];
271
271
 
272
272
  // Copilot streams `assistant.message_delta` with `data.deltaContent` chunks
273
- // before emitting the final `assistant.message`. We accumulate the deltas
274
- // for the live onChunk feed; the final `assistant.message.data.content`
275
- // value is the authoritative text.
273
+ // before emitting `assistant.message`. Tool-request messages can include
274
+ // narration ("I'll inspect...") that is only progress text, so terminal text
275
+ // comes from non-tool assistant messages or trailing deltas.
276
276
  let copilotMessageBuffer = '';
277
277
 
278
+ function _streamText(value) {
279
+ return maxTextLength ? value.slice(-maxTextLength) : value;
280
+ }
281
+
282
+ function _copilotAssistantMessageHasTools(obj) {
283
+ const requests = obj?.data?.toolRequests;
284
+ return Array.isArray(requests) && requests.length > 0;
285
+ }
286
+
278
287
  function captureEvent(obj) {
279
288
  if (!obj || typeof obj !== 'object') return;
280
289
 
@@ -322,12 +331,12 @@ function _createStreamAccumulator({
322
331
  }
323
332
  }
324
333
  if (obj.type === 'assistant.message' && typeof obj.data?.content === 'string') {
325
- // Authoritative final assistant text for this turn.
334
+ // Tool-request narration ("I'll look into this...") is progress text, not
335
+ // the final answer. Keep streaming it live, but don't let it become the
336
+ // terminal result if the process exits before a final answer message.
326
337
  const content = obj.data.content;
327
- if (content) {
328
- text = maxTextLength ? content.slice(-maxTextLength) : content;
329
- copilotMessageBuffer = '';
330
- }
338
+ if (content && !_copilotAssistantMessageHasTools(obj)) text = _streamText(content);
339
+ copilotMessageBuffer = '';
331
340
  if (Array.isArray(obj.data.toolRequests)) {
332
341
  for (const tr of obj.data.toolRequests) {
333
342
  if (tr && tr.name) {
@@ -372,6 +381,9 @@ function _createStreamAccumulator({
372
381
  const ev = runtime.parseStreamChunk(trimmed);
373
382
  if (ev) captureEvent(ev);
374
383
  }
384
+ if (copilotMessageBuffer) {
385
+ text = _streamText(copilotMessageBuffer);
386
+ }
375
387
  // Reconciliation: if any field is still missing, ask the runtime adapter
376
388
  // to re-parse the whole stdout. parseOutput() may catch a result event
377
389
  // that was malformed when streamed in chunks.
@@ -300,8 +300,10 @@ const KNOWN_EVENT_TYPES = new Set([
300
300
  * Returns { text, usage, sessionId, model } — same shape as the Claude adapter
301
301
  * so engine/lifecycle.js can consume both transparently.
302
302
  *
303
- * - text: concatenation of every `assistant.message.data.content` value
304
- * across turns (multi-turn autopilot loops emit one per turn)
303
+ * - text: concatenation of answer `assistant.message.data.content`
304
+ * values, plus trailing deltas if the CLI exits before a final
305
+ * `assistant.message`. Narration attached to tool requests is
306
+ * progress text and is excluded from the final answer.
305
307
  * - usage: mapped from the terminal `result` event. Copilot doesn't
306
308
  * report cost/tokens — those fields are NULL, not 0, so the
307
309
  * dashboard can distinguish "Copilot didn't tell us" from
@@ -322,6 +324,7 @@ function parseOutput(raw, { maxTextLength = 0 } = {}) {
322
324
  let model = null;
323
325
  let outputTokensTotal = 0;
324
326
  let turnEndCount = 0;
327
+ let pendingDeltaContent = '';
325
328
 
326
329
  for (const rawLine of safeRaw.split('\n')) {
327
330
  const line = rawLine.trim();
@@ -331,9 +334,17 @@ function parseOutput(raw, { maxTextLength = 0 } = {}) {
331
334
  if (!obj || typeof obj !== 'object') continue;
332
335
 
333
336
  const type = obj.type;
334
- if (type === 'assistant.message') {
337
+ if (type === 'assistant.message_delta') {
338
+ const delta = obj.data?.deltaContent;
339
+ if (typeof delta === 'string') pendingDeltaContent += delta;
340
+ } else if (type === 'assistant.message') {
335
341
  const content = obj.data?.content;
336
- if (typeof content === 'string' && content) messageContents.push(content);
342
+ const toolRequests = obj.data?.toolRequests;
343
+ const hasToolRequests = Array.isArray(toolRequests) && toolRequests.length > 0;
344
+ if (typeof content === 'string') {
345
+ if (content && !hasToolRequests) messageContents.push(content);
346
+ pendingDeltaContent = '';
347
+ }
337
348
  const ot = obj.data?.outputTokens;
338
349
  if (typeof ot === 'number') outputTokensTotal += ot;
339
350
  } else if (type === 'assistant.turn_end') {
@@ -366,7 +377,7 @@ function parseOutput(raw, { maxTextLength = 0 } = {}) {
366
377
  }
367
378
  }
368
379
 
369
- let text = messageContents.join('');
380
+ let text = messageContents.join('') + pendingDeltaContent;
370
381
  if (maxTextLength && text.length > maxTextLength) {
371
382
  text = text.slice(-maxTextLength);
372
383
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1598",
3
+ "version": "0.1.1600",
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"