@yemi33/minions 0.1.757 → 0.1.759

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,11 +1,13 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.757 (2026-04-09)
3
+ ## 0.1.759 (2026-04-09)
4
4
 
5
5
  ### Features
6
6
  - show branch strategy badge on PRD group header
7
7
 
8
8
  ### Fixes
9
+ - eliminate global ccSession corruption, clean up streaming state
10
+ - restore full streaming state (tools + partial text) on tab switch
9
11
  - restored thinking UX matches original exactly (same bubble, layout)
10
12
  - skip merged dependency branches instead of failing worktree setup
11
13
  - restore same thinking UX when switching back to processing tab
@@ -135,28 +135,38 @@ function ccSwitchTab(id) {
135
135
  for (var i = 0; i < tab.messages.length; i++) {
136
136
  ccAddMessage(tab.messages[i].role, tab.messages[i].html, true);
137
137
  }
138
- // If this tab is still processing, restore the same thinking UX via addMessage (identical to original)
138
+ // If this tab is still processing, restore the full streaming UX (tools, partial text, thinking)
139
139
  if (tab._sending) {
140
140
  var dotPulse = '<span style="display:inline-flex;gap:3px;margin-left:6px;vertical-align:middle"><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.2s"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.4s"></span></span>';
141
141
  var restoreStart = tab._sendStartedAt || Date.now();
142
142
  var phases = [[0,'Thinking...'],[3000,'Reading minions context...'],[8000,'Analyzing...'],[15000,'Using tools to dig deeper...'],[30000,'Still working (multi-turn)...'],[60000,'Deep research in progress...']];
143
- function _restoreThinkingHtml() {
143
+ function _restoreStreamHtml() {
144
+ var html = '';
145
+ var tools = tab._toolsUsed || [];
146
+ if (tools.length > 0) {
147
+ html += '<div style="margin-bottom:6px">';
148
+ tools.forEach(function(t) { html += '<div style="color:var(--blue);font-size:11px">\uD83D\uDD27 ' + escHtml(t) + '</div>'; });
149
+ html += '</div>';
150
+ }
151
+ var text = tab._streamedText || '';
152
+ if (text) html += renderMd(text);
144
153
  var ms = Date.now() - restoreStart;
145
154
  var label = 'Thinking...';
146
155
  for (var pi = phases.length - 1; pi >= 0; pi--) { if (ms >= phases[pi][0]) { label = phases[pi][1]; break; } }
147
156
  var secs = Math.floor(ms / 1000);
148
- return '<div style="display:flex;align-items:center;gap:6px"><span style="color:var(--muted);font-size:11px">' + label + '</span>' + dotPulse + '<span style="margin-left:auto;font-size:10px;color:var(--muted)">' + secs + 's</span>' +
157
+ html += '<div style="margin-top:' + (text ? '6px' : '0') + ';display:flex;align-items:center;gap:6px"><span style="color:var(--muted);font-size:11px">' + label + '</span>' + dotPulse + '<span style="margin-left:auto;font-size:10px;color:var(--muted)">' + secs + 's</span>' +
149
158
  '<button onclick="ccAbort()" style="font-size:9px;padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer;margin-left:4px">Stop</button></div>';
159
+ return html;
150
160
  }
151
- // Create as assistant message bubble (same as original)
152
- ccAddMessage('assistant', _restoreThinkingHtml(), true);
161
+ ccAddMessage('assistant', _restoreStreamHtml(), true);
153
162
  var restoreDiv = el.lastElementChild;
154
163
  restoreDiv.id = 'cc-restore-thinking';
155
164
  el.scrollTop = el.scrollHeight;
156
165
  var restoreInterval = setInterval(function() {
157
166
  var re = document.getElementById('cc-restore-thinking');
158
167
  if (!re || !tab._sending) { clearInterval(restoreInterval); if (re) re.remove(); return; }
159
- re.innerHTML = (typeof llmCopyBtn === 'function' ? '' : '') + _restoreThinkingHtml();
168
+ re.innerHTML = _restoreStreamHtml();
169
+ if (el.scrollHeight - el.scrollTop - el.clientHeight < 150) el.scrollTop = el.scrollHeight;
160
170
  }, 1000);
161
171
  }
162
172
  ccRenderTabBar();
@@ -448,8 +458,10 @@ async function _ccDoSend(message, skipUserMsg) {
448
458
  ];
449
459
 
450
460
  // Streaming state — declared before try so updateStreamDiv works during fetch
461
+ // Also saved on tab for restore when switching back
451
462
  var streamedText = '';
452
463
  var toolsUsed = [];
464
+ if (activeTab) { activeTab._streamedText = ''; activeTab._toolsUsed = []; }
453
465
 
454
466
  // Get active tab's sessionId to send with request
455
467
  var activeTab = _ccActiveTab();
@@ -526,9 +538,11 @@ async function _ccDoSend(message, skipUserMsg) {
526
538
  var evt = JSON.parse(line.slice(6));
527
539
  if (evt.type === 'chunk') {
528
540
  streamedText = evt.text;
541
+ if (activeTab) activeTab._streamedText = streamedText;
529
542
  updateStreamDiv();
530
543
  } else if (evt.type === 'tool') {
531
544
  toolsUsed.push(evt.name);
545
+ if (activeTab) activeTab._toolsUsed = toolsUsed.slice();
532
546
  updateStreamDiv();
533
547
  if (msgs.scrollHeight - msgs.scrollTop - msgs.clientHeight < 150) msgs.scrollTop = msgs.scrollHeight;
534
548
  } else if (evt.type === 'done') {
@@ -611,7 +625,7 @@ async function _ccDoSend(message, skipUserMsg) {
611
625
  ' <button onclick="ccNewTab()" style="margin-top:6px;padding:4px 12px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--muted);cursor:pointer;font-size:11px">New Session</button>');
612
626
  }
613
627
  } finally {
614
- if (activeTab) { activeTab._sending = false; activeTab._abortController = null; }
628
+ if (activeTab) { activeTab._sending = false; activeTab._abortController = null; delete activeTab._streamedText; delete activeTab._toolsUsed; delete activeTab._sendStartedAt; }
615
629
  _ccSending = (_ccTabs.some(function(t) { return t._sending; }));
616
630
  try { clearInterval(phaseTimer); } catch { /* may not be defined if error before reader */ }
617
631
  try { localStorage.removeItem('cc-sending'); } catch {}
package/dashboard.js CHANGED
@@ -3329,16 +3329,10 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3329
3329
  req.on('close', () => { ccInFlightTabs.delete(tabId); if (_ccStreamAbort) _ccStreamAbort(); });
3330
3330
 
3331
3331
  try {
3332
- // Session management — per-tab: use sessionId from request if provided
3332
+ // Session management — per-tab: use sessionId from request, don't mutate global ccSession
3333
3333
  const tabSessionId = body.sessionId || null;
3334
- if (tabSessionId) {
3335
- // Resume the tab's specific session
3336
- ccSession = { sessionId: tabSessionId, createdAt: ccSession.createdAt, lastActiveAt: Date.now(), turnCount: ccSession.turnCount || 0, _promptHash: _ccPromptHash };
3337
- } else if (!ccSessionValid()) {
3338
- ccSession = { sessionId: null, createdAt: null, lastActiveAt: null, turnCount: 0 };
3339
- }
3340
- const wasResume = !!tabSessionId || !!(ccSessionValid() && ccSession.sessionId);
3341
- const sessionId = tabSessionId || (wasResume ? ccSession.sessionId : null);
3334
+ const wasResume = !!tabSessionId;
3335
+ const sessionId = tabSessionId || null;
3342
3336
  const preamble = wasResume ? '' : buildCCStatePreamble();
3343
3337
  const prompt = (preamble ? preamble + '\n\n---\n\n' : '') + body.message;
3344
3338
 
@@ -3367,42 +3361,33 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3367
3361
  const stderrTail = (result.stderr || '').trim().split('\n').filter(Boolean).slice(-3).join(' | ');
3368
3362
  console.error(`[CC-stream] Failed: code=${result.code}, stderr=${(result.stderr || '').slice(0, 500)}, stdout_tail=${(result.raw || '').slice(-500)}`);
3369
3363
  // If resuming a session failed, auto-reset so next attempt starts fresh
3370
- let retryHint;
3371
- if (wasResume && result.code !== 0) {
3372
- ccSession = { sessionId: null, createdAt: null, lastActiveAt: null, turnCount: 0 };
3373
- safeWrite(path.join(ENGINE_DIR, 'cc-session.json'), ccSession);
3374
- retryHint = 'Session was reset send your message again to start fresh.';
3375
- } else {
3376
- retryHint = ccSession.sessionId
3377
- ? 'Your session is still active — just send your message again to retry.'
3378
- : 'Try clicking **New Session** and sending your message again.';
3379
- }
3380
- res.write('data: ' + JSON.stringify({ type: 'done', text: `I had trouble processing that ${debugInfo}. ${stderrTail ? 'Detail: ' + stderrTail : ''}\n\n${retryHint}`, actions: [], sessionId: ccSession.sessionId }) + '\n\n');
3364
+ const retryHint = wasResume && result.code !== 0
3365
+ ? 'Session was reset send your message again to start fresh.'
3366
+ : 'Send your message again to retry.';
3367
+ // Return null sessionId on resume failure so client clears the stale session
3368
+ const errorSessionId = (wasResume && result.code !== 0) ? null : tabSessionId;
3369
+ res.write('data: ' + JSON.stringify({ type: 'done', text: `I had trouble processing that ${debugInfo}. ${stderrTail ? 'Detail: ' + stderrTail : ''}\n\n${retryHint}`, actions: [], sessionId: errorSessionId }) + '\n\n');
3381
3370
  res.end();
3382
3371
  return;
3383
3372
  }
3384
3373
 
3385
3374
  // Update session
3375
+ // Persist tab→session mapping (no global ccSession mutation)
3386
3376
  const now = Date.now();
3387
- if (result.sessionId) {
3388
- ccSession = { sessionId: result.sessionId, createdAt: ccSession.createdAt || now, lastActiveAt: now, turnCount: (ccSession.turnCount || 0) + 1, _promptHash: _ccPromptHash };
3389
- safeWrite(path.join(ENGINE_DIR, 'cc-session.json'), ccSession);
3390
- }
3391
-
3392
- // Persist tab→session mapping if tabId provided
3377
+ const responseSessionId = result.sessionId || tabSessionId;
3393
3378
  const tabId = body.tabId;
3394
- if (tabId && ccSession.sessionId) {
3379
+ if (tabId && responseSessionId) {
3395
3380
  try {
3396
3381
  const sessions = shared.safeJsonArr(CC_SESSIONS_PATH);
3397
3382
  const existing = sessions.find(s => s.id === tabId);
3398
3383
  const preview = (body.message || '').slice(0, 80);
3399
3384
  if (existing) {
3400
- existing.sessionId = ccSession.sessionId;
3385
+ existing.sessionId = responseSessionId;
3401
3386
  existing.lastActiveAt = new Date(now).toISOString();
3402
- existing.turnCount = ccSession.turnCount;
3387
+ existing.turnCount = (existing.turnCount || 0) + 1;
3403
3388
  existing.preview = preview;
3404
3389
  } else {
3405
- sessions.push({ id: tabId, title: (body.message || 'New chat').slice(0, 40), sessionId: ccSession.sessionId, createdAt: new Date(now).toISOString(), lastActiveAt: new Date(now).toISOString(), turnCount: ccSession.turnCount, preview });
3390
+ sessions.push({ id: tabId, title: (body.message || 'New chat').slice(0, 40), sessionId: responseSessionId, createdAt: new Date(now).toISOString(), lastActiveAt: new Date(now).toISOString(), turnCount: 1, preview });
3406
3391
  }
3407
3392
  safeWrite(CC_SESSIONS_PATH, sessions);
3408
3393
  } catch { /* non-critical */ }
@@ -3410,7 +3395,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3410
3395
 
3411
3396
  // Send final result with actions
3412
3397
  const { text: displayText, actions } = parseCCActions(result.text);
3413
- res.write('data: ' + JSON.stringify({ type: 'done', text: displayText, actions, sessionId: ccSession.sessionId, newSession: !wasResume }) + '\n\n');
3398
+ res.write('data: ' + JSON.stringify({ type: 'done', text: displayText, actions, sessionId: responseSessionId, newSession: !wasResume }) + '\n\n');
3414
3399
  res.end();
3415
3400
  } finally {
3416
3401
  ccInFlightTabs.delete(tabId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.757",
3
+ "version": "0.1.759",
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"