@yemi33/minions 0.1.1583 → 0.1.1584

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,6 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1583 (2026-04-28)
3
+ ## 0.1.1584 (2026-04-28)
4
+
5
+ ### Fixes
6
+ - stack chain-of-thought above progress block (was side-by-side)
4
7
 
5
8
  ### Other
6
9
  - Keep doc chat collapsed while streaming
@@ -27,8 +27,8 @@ let _qaProcessing = false; // true while waiting for response
27
27
  let _qaAbortController = null;
28
28
  let _qaQueue = []; // queued messages while processing
29
29
  const QA_QUEUE_CAP = 10; // max queued messages
30
+ const QA_STREAM_STALL_MS = 90000; // abort doc-chat if heartbeats continue but no chunk/tool progress arrives
30
31
  let _qaSessionKey = ''; // key for current conversation (title or filePath)
31
- let _qaThreadCollapsed = false; // sticky while streaming so collapse doesn't bounce open on updates
32
32
 
33
33
  function _renderQaUserMessage(thread, message, selection) {
34
34
  let qHtml = '<div class="modal-qa-q">' + escHtml(message);
@@ -38,7 +38,7 @@ function _renderQaUserMessage(thread, message, selection) {
38
38
  qHtml += '</div>';
39
39
  thread.insertAdjacentHTML('beforeend', qHtml);
40
40
  thread.scrollTop = thread.scrollHeight;
41
- _showThreadWrap(true);
41
+ _showThreadWrap();
42
42
  }
43
43
  const _qaSessions = new Map(); // persist conversations across modal open/close {key → {history, threadHtml}}
44
44
  const _qaRuntime = new Map(); // key → {history, processing, abortController, queue}
@@ -199,28 +199,29 @@ function _qaBuildAssistantHtml(text, opts) {
199
199
 
200
200
  function _qaBuildLiveProgressHtml(loadingId, label, elapsedSeconds, streamedText, toolsUsed, queueCount) {
201
201
  const qaQueueBadge = queueCount > 0 ? ' <span style="font-size:9px;color:var(--muted);background:var(--surface);padding:1px 5px;border-radius:8px;border:1px solid var(--border)">+' + queueCount + ' queued</span>' : '';
202
- let html = '';
202
+ // Wrap in a column-flex container so chain-of-thought (tool calls) stack
203
+ // vertically on top and the progress block sits at the bottom. Overrides the
204
+ // parent .modal-qa-loading row-flex (which is right for the simple
205
+ // "Thinking..." initial state but wrong once tools/streamed text appear).
206
+ let html = '<div style="display:flex;flex-direction:column;align-items:stretch;gap:6px;width:100%">';
203
207
  if (toolsUsed && toolsUsed.length > 0) {
204
- html += '<div style="margin-bottom:6px">';
208
+ html += '<div style="display:flex;flex-direction:column;gap:2px">';
205
209
  toolsUsed.forEach(function(t) {
206
210
  const name = typeof t === 'string' ? t : t.name;
207
211
  const input = typeof t === 'string' ? {} : (t.input || {});
208
- html += '<div style="color:var(--muted);font-size:10px;font-family:monospace"><span style="flex-shrink:0">&#9679;</span> ' + formatToolSummary(name, input) + '</div>';
212
+ html += '<div style="color:var(--muted);font-size:10px;font-family:monospace;display:flex;align-items:flex-start;gap:6px"><span style="flex-shrink:0">&#9679;</span><span style="word-break:break-all">' + formatToolSummary(name, input) + '</span></div>';
209
213
  });
210
214
  html += '</div>';
211
215
  }
212
- if (streamedText) html += '<div style="margin-bottom:6px">' + renderMd(streamedText) + '</div>';
213
- html += '<div style="display:flex;flex-direction:column;align-items:flex-start;gap:6px">' +
214
- '<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap">' +
215
- '<span class="dot-pulse"><span></span><span></span><span></span></span> ' +
216
- '<span id="' + loadingId + '-text">' + escHtml(label) + '</span>' +
217
- '</div>' +
218
- '<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap">' +
219
- '<span id="' + loadingId + '-time" style="font-size:10px;color:var(--muted)">' + elapsedSeconds + 's</span>' +
220
- '<button onclick="qaAbort()" style="font-size:9px;padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer">Stop</button>' +
221
- qaQueueBadge +
222
- '</div>' +
223
- '</div>';
216
+ if (streamedText) html += '<div>' + renderMd(streamedText) + '</div>';
217
+ html += '<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">' +
218
+ '<span class="dot-pulse"><span></span><span></span><span></span></span>' +
219
+ '<span id="' + loadingId + '-text">' + escHtml(label) + '</span>' +
220
+ '<span id="' + loadingId + '-time" style="font-size:10px;color:var(--muted)">' + elapsedSeconds + 's</span>' +
221
+ '<button onclick="qaAbort()" style="font-size:9px;padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer">Stop</button>' +
222
+ qaQueueBadge +
223
+ '</div>';
224
+ html += '</div>';
224
225
  return html;
225
226
  }
226
227
 
@@ -291,7 +292,6 @@ function _initQaSession() {
291
292
  if (!key || _qaSessionKey === key) return;
292
293
  if (_qaSessionKey && _qaSessionKey !== key) _qaSaveActiveSessionState();
293
294
  _qaSessionKey = key;
294
- _qaThreadCollapsed = false;
295
295
  const card = findCardForFile(_modalFilePath);
296
296
  if (card) clearNotifBadge(card);
297
297
  var prior = _qaSessions.get(key);
@@ -307,7 +307,7 @@ function _initQaSession() {
307
307
  });
308
308
  }
309
309
  if (prior.filePath) _modalFilePath = prior.filePath;
310
- _showThreadWrap(true);
310
+ _showThreadWrap();
311
311
  requestAnimationFrame(function() {
312
312
  var thread = document.getElementById('modal-qa-thread');
313
313
  if (thread) thread.scrollTop = thread.scrollHeight;
@@ -333,7 +333,6 @@ function clearQaConversation() {
333
333
  _qaQueue = [];
334
334
  _qaProcessing = false;
335
335
  _qaAbortController = null;
336
- _qaThreadCollapsed = false;
337
336
  document.getElementById('modal-qa-thread').innerHTML = '';
338
337
  var wrap = document.getElementById('modal-qa-thread-wrap');
339
338
  var expandBar = document.getElementById('qa-expand-bar');
@@ -433,6 +432,21 @@ async function _processQaMessage(message, selection, opts) {
433
432
  : [[0,'Thinking...'],[3000,'Reading document...'],[8000,'Analyzing...'],[20000,'Still working...'],[60000,'Taking a while...']];
434
433
  let streamedText = '';
435
434
  let toolsUsed = [];
435
+ let _qaStreamStalled = false;
436
+ let _qaStallTimer = null;
437
+ function _clearQaStreamWatchdog() {
438
+ if (_qaStallTimer) {
439
+ clearTimeout(_qaStallTimer);
440
+ _qaStallTimer = null;
441
+ }
442
+ }
443
+ function _resetQaStreamWatchdog() {
444
+ _clearQaStreamWatchdog();
445
+ _qaStallTimer = setTimeout(() => {
446
+ _qaStreamStalled = true;
447
+ try { abortController.abort(); } catch {}
448
+ }, QA_STREAM_STALL_MS);
449
+ }
436
450
  function _qaProgressLabel(elapsed) {
437
451
  let label = qaPhases[0][1];
438
452
  for (let i = qaPhases.length - 1; i >= 0; i--) {
@@ -471,6 +485,7 @@ async function _processQaMessage(message, selection, opts) {
471
485
  _qaRenderProgress(false);
472
486
  }, 500);
473
487
  _qaRenderProgress(false);
488
+ _resetQaStreamWatchdog();
474
489
 
475
490
  try {
476
491
  const res = await fetch('/api/doc-chat/stream', {
@@ -507,11 +522,13 @@ async function _processQaMessage(message, selection, opts) {
507
522
  if (!evt || !evt.type) return;
508
523
  if (evt.type === 'heartbeat') return;
509
524
  if (evt.type === 'chunk') {
525
+ _resetQaStreamWatchdog();
510
526
  streamedText = evt.text || '';
511
527
  _qaRenderProgress(true);
512
528
  return;
513
529
  }
514
530
  if (evt.type === 'tool') {
531
+ _resetQaStreamWatchdog();
515
532
  toolsUsed.push({ name: evt.name, input: evt.input || {} });
516
533
  _qaRenderProgress(true);
517
534
  return;
@@ -519,6 +536,7 @@ async function _processQaMessage(message, selection, opts) {
519
536
  if (evt.type === 'done') {
520
537
  terminalEventSeen = true;
521
538
  clearInterval(qaTimer);
539
+ _clearQaStreamWatchdog();
522
540
  const qaElapsed = Math.round((Date.now() - qaStartTime) / 1000);
523
541
  const borderColor = evt.edited ? 'var(--green)' : 'var(--blue)';
524
542
  const suffix = evt.edited ? '\n\n\u2713 Document saved.' : '';
@@ -565,7 +583,10 @@ async function _processQaMessage(message, selection, opts) {
565
583
  });
566
584
  return;
567
585
  }
568
- if (evt.type === 'error') throw new Error(evt.error || 'Failed');
586
+ if (evt.type === 'error') {
587
+ _clearQaStreamWatchdog();
588
+ throw new Error(evt.error || 'Failed');
589
+ }
569
590
  }
570
591
 
571
592
  while (true) {
@@ -591,8 +612,14 @@ async function _processQaMessage(message, selection, opts) {
591
612
  if (!terminalEventSeen) throw new Error('The response stream ended before completion.');
592
613
  } catch (e) {
593
614
  clearInterval(qaTimer);
615
+ _clearQaStreamWatchdog();
594
616
  const qaElapsedExc = Math.round((Date.now() - qaStartTime) / 1000);
595
- const messageHtml = e.name === 'AbortError'
617
+ const stallMessage = 'Doc chat stalled with no tool or text progress for 90s.';
618
+ const messageHtml = _qaStreamStalled
619
+ ? (streamedText
620
+ ? _qaBuildAssistantHtml(streamedText + '\n\nError: ' + stallMessage, { borderColor: 'var(--red)', elapsed: qaElapsedExc })
621
+ : _qaBuildAssistantHtml('Error: ' + stallMessage, { color: 'var(--red)', isError: true, elapsed: qaElapsedExc }))
622
+ : e.name === 'AbortError'
596
623
  ? (streamedText
597
624
  ? _qaBuildAssistantHtml(streamedText + '\n\n_Stopped._', { borderColor: 'var(--muted)', elapsed: qaElapsedExc })
598
625
  : _qaBuildAssistantHtml('Stopped', { color: 'var(--muted)', isError: true, elapsed: qaElapsedExc }))
@@ -674,18 +701,15 @@ function toggleDocChat() {
674
701
  var expandBar = document.getElementById('qa-expand-bar');
675
702
  if (!wrap) return;
676
703
  var visible = wrap.style.display !== 'none';
677
- var nextVisible = !visible;
678
- _qaThreadCollapsed = !nextVisible;
679
- wrap.style.display = nextVisible ? '' : 'none';
680
- if (expandBar) expandBar.style.display = nextVisible ? 'none' : '';
704
+ wrap.style.display = visible ? 'none' : '';
705
+ if (expandBar) expandBar.style.display = visible ? '' : 'none';
681
706
  }
682
707
 
683
- function _showThreadWrap(forceExpand) {
708
+ function _showThreadWrap() {
684
709
  var wrap = document.getElementById('modal-qa-thread-wrap');
685
710
  var expandBar = document.getElementById('qa-expand-bar');
686
- if (forceExpand) _qaThreadCollapsed = false;
687
- if (wrap) wrap.style.display = _qaThreadCollapsed ? 'none' : '';
688
- if (expandBar) expandBar.style.display = _qaThreadCollapsed ? '' : 'none';
711
+ if (wrap) wrap.style.display = '';
712
+ if (expandBar) expandBar.style.display = 'none';
689
713
  }
690
714
 
691
715
  // ── Drag-to-resize doc chat thread ──────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1583",
3
+ "version": "0.1.1584",
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"