@yemi33/minions 0.1.758 → 0.1.760

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.758 (2026-04-09)
3
+ ## 0.1.760 (2026-04-10)
4
4
 
5
5
  ### Features
6
6
  - show branch strategy badge on PRD group header
7
7
 
8
8
  ### Fixes
9
+ - prevent completed SSE stream from aborting other tab's LLM process
10
+ - eliminate global ccSession corruption, clean up streaming state
9
11
  - restore full streaming state (tools + partial text) on tab switch
10
12
  - restored thinking UX matches original exactly (same bubble, layout)
11
13
  - skip merged dependency branches instead of failing worktree setup
@@ -625,7 +625,7 @@ async function _ccDoSend(message, skipUserMsg) {
625
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>');
626
626
  }
627
627
  } finally {
628
- 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; }
629
629
  _ccSending = (_ccTabs.some(function(t) { return t._sending; }));
630
630
  try { clearInterval(phaseTimer); } catch { /* may not be defined if error before reader */ }
631
631
  try { localStorage.removeItem('cc-sending'); } catch {}
package/dashboard.js CHANGED
@@ -3325,20 +3325,21 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3325
3325
 
3326
3326
  res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' });
3327
3327
  let _ccStreamAbort = null;
3328
+ let _ccStreamEnded = false;
3328
3329
  // Kill LLM process immediately if client disconnects mid-stream
3329
- req.on('close', () => { ccInFlightTabs.delete(tabId); if (_ccStreamAbort) _ccStreamAbort(); });
3330
+ req.on('close', () => {
3331
+ ccInFlightTabs.delete(tabId);
3332
+ if (!_ccStreamEnded && _ccStreamAbort) {
3333
+ console.log(`[CC-stream] Client disconnected for tab ${tabId} — aborting LLM`);
3334
+ _ccStreamAbort();
3335
+ }
3336
+ });
3330
3337
 
3331
3338
  try {
3332
- // Session management — per-tab: use sessionId from request if provided
3339
+ // Session management — per-tab: use sessionId from request, don't mutate global ccSession
3333
3340
  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);
3341
+ const wasResume = !!tabSessionId;
3342
+ const sessionId = tabSessionId || null;
3342
3343
  const preamble = wasResume ? '' : buildCCStatePreamble();
3343
3344
  const prompt = (preamble ? preamble + '\n\n---\n\n' : '') + body.message;
3344
3345
 
@@ -3367,42 +3368,33 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3367
3368
  const stderrTail = (result.stderr || '').trim().split('\n').filter(Boolean).slice(-3).join(' | ');
3368
3369
  console.error(`[CC-stream] Failed: code=${result.code}, stderr=${(result.stderr || '').slice(0, 500)}, stdout_tail=${(result.raw || '').slice(-500)}`);
3369
3370
  // 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');
3381
- res.end();
3371
+ const retryHint = wasResume && result.code !== 0
3372
+ ? 'Session was reset send your message again to start fresh.'
3373
+ : 'Send your message again to retry.';
3374
+ // Return null sessionId on resume failure so client clears the stale session
3375
+ const errorSessionId = (wasResume && result.code !== 0) ? null : tabSessionId;
3376
+ 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');
3377
+ _ccStreamEnded = true; res.end();
3382
3378
  return;
3383
3379
  }
3384
3380
 
3385
3381
  // Update session
3382
+ // Persist tab→session mapping (no global ccSession mutation)
3386
3383
  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
3384
+ const responseSessionId = result.sessionId || tabSessionId;
3393
3385
  const tabId = body.tabId;
3394
- if (tabId && ccSession.sessionId) {
3386
+ if (tabId && responseSessionId) {
3395
3387
  try {
3396
3388
  const sessions = shared.safeJsonArr(CC_SESSIONS_PATH);
3397
3389
  const existing = sessions.find(s => s.id === tabId);
3398
3390
  const preview = (body.message || '').slice(0, 80);
3399
3391
  if (existing) {
3400
- existing.sessionId = ccSession.sessionId;
3392
+ existing.sessionId = responseSessionId;
3401
3393
  existing.lastActiveAt = new Date(now).toISOString();
3402
- existing.turnCount = ccSession.turnCount;
3394
+ existing.turnCount = (existing.turnCount || 0) + 1;
3403
3395
  existing.preview = preview;
3404
3396
  } 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 });
3397
+ 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
3398
  }
3407
3399
  safeWrite(CC_SESSIONS_PATH, sessions);
3408
3400
  } catch { /* non-critical */ }
@@ -3410,15 +3402,15 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3410
3402
 
3411
3403
  // Send final result with actions
3412
3404
  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');
3414
- res.end();
3405
+ res.write('data: ' + JSON.stringify({ type: 'done', text: displayText, actions, sessionId: responseSessionId, newSession: !wasResume }) + '\n\n');
3406
+ _ccStreamEnded = true; res.end();
3415
3407
  } finally {
3416
3408
  ccInFlightTabs.delete(tabId);
3417
3409
  }
3418
3410
  } catch (e) {
3419
3411
  ccInFlightTabs.delete(tabId);
3420
3412
  try { res.write('data: ' + JSON.stringify({ type: 'error', error: e.message }) + '\n\n'); } catch {}
3421
- try { res.end(); } catch {}
3413
+ _ccStreamEnded = true; try { res.end(); } catch {}
3422
3414
  }
3423
3415
  }
3424
3416
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.758",
3
+ "version": "0.1.760",
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"