@yemi33/minions 0.1.1932 → 0.1.1934

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/bin/minions.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * minions add <project-dir> Link a project (interactive)
9
9
  * minions remove <project-dir> Unlink a project
10
10
  * minions list List linked projects
11
- * minions update Update to latest version
11
+ * minions update [--no-wait] Update to latest version (--no-wait backgrounds the post-update restart)
12
12
  * minions version Show installed and package versions
13
13
  * minions doctor Check prerequisites and runtime health
14
14
  * minions restart [--open] Start engine + dashboard (--open forces a new browser tab)
@@ -604,6 +604,28 @@ function runPostUpdateRestart() {
604
604
  process.exit(1);
605
605
  }
606
606
 
607
+ // Fire-and-forget variant for `minions update --no-wait`. The npm + init phases
608
+ // have already completed synchronously, so package-install failures surface
609
+ // normally; only the long-tail engine/dashboard restart + health check is
610
+ // backgrounded. stdio is redirected to a log so the user can postmortem if the
611
+ // restart fails after the parent has exited.
612
+ function runPostUpdateRestartDetached() {
613
+ const initScript = path.join(PKG_ROOT, 'bin', 'minions.js');
614
+ const out = _openStdioLog('update-restart-stdio.log');
615
+ const err = _openStdioLog('update-restart-stdio.log');
616
+ const proc = spawn(process.execPath, [initScript, 'restart'], {
617
+ cwd: process.cwd(),
618
+ env: { ...process.env, MINIONS_HOME },
619
+ stdio: ['ignore', out, err],
620
+ detached: true,
621
+ windowsHide: true,
622
+ });
623
+ proc.unref();
624
+ const logPath = path.join(MINIONS_HOME, 'engine', 'update-restart-stdio.log');
625
+ console.log(` Restart spawned in background (PID: ${proc.pid}); logs: ${logPath}`);
626
+ console.log(` Verify with: minions status\n`);
627
+ }
628
+
607
629
  // ─── Version command ────────────────────────────────────────────────────────
608
630
 
609
631
  function showVersion() {
@@ -668,7 +690,7 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
668
690
 
669
691
  Setup:
670
692
  minions init Bootstrap ~/.minions/ (first time)
671
- minions update Update to latest version (npm update + one restart)
693
+ minions update [--no-wait] Update to latest version (--no-wait backgrounds the restart)
672
694
  minions version Show installed vs package version
673
695
  minions doctor Check prerequisites and runtime health
674
696
  minions add <project-dir> Link a project (interactive)
@@ -707,6 +729,7 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
707
729
  } else if (cmd === 'init') {
708
730
  init();
709
731
  } else if (cmd === 'update') {
732
+ const noWait = rest.includes('--no-wait') || rest.includes('--detach');
710
733
  console.log('\n Updating Minions...\n');
711
734
  // Dev/symlink installs: PKG_ROOT === MINIONS_HOME — npm update is a no-op (symlink already
712
735
  // points to the repo), and `minions init --force` would fail (cwd/.minions is inside PKG_ROOT).
@@ -726,9 +749,15 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
726
749
  // the global shim while npm is still settling the updated install.
727
750
  runPostUpdateInit();
728
751
  }
729
- // Restart engine + dashboard so they pick up the new code
752
+ // Restart engine + dashboard so they pick up the new code. With --no-wait the
753
+ // restart is spawned detached so the caller (e.g. a DevBox maintenance task)
754
+ // can return immediately without holding the parent open for the health poll.
730
755
  console.log('\n Restarting engine and dashboard...\n');
731
- runPostUpdateRestart();
756
+ if (noWait) {
757
+ runPostUpdateRestartDetached();
758
+ } else {
759
+ runPostUpdateRestart();
760
+ }
732
761
  } else if (cmd === 'version' || cmd === '--version' || cmd === '-v') {
733
762
  showVersion();
734
763
  } else if (cmd === 'add' || cmd === 'remove' || cmd === 'list' || cmd === 'scan') {
package/dashboard.js CHANGED
@@ -6368,7 +6368,28 @@ What would you like to discuss or change? When you're happy, say "approve" and I
6368
6368
  let accumulated = '';
6369
6369
  let sessionHandle = null;
6370
6370
  let resolveResult;
6371
+ // Per-turn phase timing — emitted as one structured [cc-timing] log line
6372
+ // so we can attribute Copilot CC slowness (e.g. 36s) to spawn vs first-byte
6373
+ // vs streaming vs trailing inference. Parallels engine.js's [spawn-timing].
6374
+ const _tStart = Date.now();
6375
+ let _tFirstByte = null;
6376
+ let _tFirstTool = null;
6377
+ let _chunkCount = 0;
6378
+ let _toolUseCount = 0;
6371
6379
  const promise = new Promise((resolve) => { resolveResult = resolve; });
6380
+ const _emitTimingLog = (lifecycle, sessionReady, streamEnd, outcome) => {
6381
+ try {
6382
+ const _diff = (a, b) => (a != null && b != null) ? (b - a) : null;
6383
+ const timings = {
6384
+ get_session: _diff(_tStart, sessionReady),
6385
+ first_byte: _diff(sessionReady, _tFirstByte),
6386
+ first_tool: _diff(sessionReady, _tFirstTool),
6387
+ stream: _diff(sessionReady, streamEnd),
6388
+ total: _diff(_tStart, streamEnd),
6389
+ };
6390
+ shared.log('info', `[cc-timing] tab=${resolvedTabId} lifecycle=${lifecycle || 'unknown'} outcome=${outcome} chunks=${_chunkCount} tools=${_toolUseCount} ${JSON.stringify(timings)}`);
6391
+ } catch { /* telemetry is best-effort */ }
6392
+ };
6372
6393
  promise.abort = () => {
6373
6394
  cancelled = true;
6374
6395
  try { sessionHandle && sessionHandle.cancel(); } catch { /* swallow */ }
@@ -6383,6 +6404,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
6383
6404
  systemPromptHash: _ccPromptHash,
6384
6405
  });
6385
6406
  } catch (err) {
6407
+ _emitTimingLog(null, null, Date.now(), 'spawn-failed');
6386
6408
  return resolveResult({
6387
6409
  text: '',
6388
6410
  sessionId: null,
@@ -6392,19 +6414,26 @@ What would you like to discuss or change? When you're happy, say "approve" and I
6392
6414
  stderr: String((err && err.message) || err || 'cc-worker-pool spawn failed'),
6393
6415
  });
6394
6416
  }
6417
+ const _tSessionReady = Date.now();
6418
+ const _lifecycle = sessionHandle.lifecycle || 'unknown';
6395
6419
  if (cancelled) {
6396
6420
  try { sessionHandle.cancel(); } catch { /* swallow */ }
6421
+ _emitTimingLog(_lifecycle, _tSessionReady, Date.now(), 'cancelled-pre-stream');
6397
6422
  return resolveResult({ text: accumulated, sessionId: sessionHandle.sessionId, code: 0, usage: {}, raw: accumulated, stderr: '' });
6398
6423
  }
6399
6424
  await sessionHandle.stream(prompt, {
6400
6425
  systemPromptText: systemPrompt,
6401
6426
  onChunk: (delta) => {
6427
+ if (_tFirstByte == null) _tFirstByte = Date.now();
6428
+ _chunkCount += 1;
6402
6429
  accumulated += delta;
6403
6430
  _touchCcLiveStream(liveState);
6404
6431
  liveState.text = accumulated;
6405
6432
  if (liveState.writer) liveState.writer({ type: 'chunk', text: accumulated });
6406
6433
  },
6407
6434
  onToolUse: (name, input) => {
6435
+ if (_tFirstTool == null) _tFirstTool = Date.now();
6436
+ _toolUseCount += 1;
6408
6437
  _touchCcLiveStream(liveState);
6409
6438
  const safeInput = input || {};
6410
6439
  if (Array.isArray(toolUses)) toolUses.push({ name, input: safeInput });
@@ -6412,9 +6441,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
6412
6441
  if (liveState.writer) liveState.writer({ type: 'tool', name, input: _lightToolInput(safeInput) });
6413
6442
  },
6414
6443
  onDone: () => {
6444
+ _emitTimingLog(_lifecycle, _tSessionReady, Date.now(), 'done');
6415
6445
  resolveResult({ text: accumulated, sessionId: sessionHandle.sessionId, code: 0, usage: {}, raw: accumulated, stderr: '' });
6416
6446
  },
6417
6447
  onError: (err) => {
6448
+ _emitTimingLog(_lifecycle, _tSessionReady, Date.now(), cancelled ? 'cancelled' : 'error');
6418
6449
  resolveResult({
6419
6450
  text: accumulated,
6420
6451
  sessionId: sessionHandle.sessionId,
@@ -483,6 +483,12 @@ async function getSession({ tabId, model, effort, mcpServers, systemPromptHash,
483
483
  if (!tabId) throw new Error('cc-worker-pool.getSession: tabId is required');
484
484
  const mcpServersHash = _hashMcpServers(mcpServers);
485
485
  let worker = _tabs.get(tabId);
486
+ // Track which lifecycle path we took so the dashboard's [cc-timing] log can
487
+ // attribute warm-reuse vs new-session vs cold-spawn. One of:
488
+ // 'warm-reuse' — proc + session both reused (no spawn, no session/new)
489
+ // 'new-session' — proc reused, fresh session/new (sysprompt hash changed)
490
+ // 'cold-spawn' — fresh proc + initialize + session/new
491
+ let lifecycle = 'warm-reuse';
486
492
 
487
493
  if (worker) {
488
494
  if (worker.killed) {
@@ -501,6 +507,7 @@ async function getSession({ tabId, model, effort, mcpServers, systemPromptHash,
501
507
  // caller has rotated in (Bug B / issue #2479).
502
508
  await worker.newSession({ mcpServers, systemPromptHash, model, effort });
503
509
  worker.lastUsedAt = _internals.now();
510
+ lifecycle = 'new-session';
504
511
  } else {
505
512
  // Warm reuse — only update bookkeeping. model/effort changes on a
506
513
  // warm session are tracked here but not pushed via configOptions in
@@ -524,12 +531,14 @@ async function getSession({ tabId, model, effort, mcpServers, systemPromptHash,
524
531
  try { worker.close(); } catch { /* already torn down */ }
525
532
  throw err;
526
533
  }
534
+ lifecycle = 'cold-spawn';
527
535
  }
528
536
 
529
537
  _ensureReaper();
530
538
 
531
539
  return {
532
540
  sessionId: worker.sessionId,
541
+ lifecycle,
533
542
  stream: (promptText, opts) => worker.stream(promptText, opts),
534
543
  cancel: () => worker.cancel(),
535
544
  close: () => closeTab(tabId),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1932",
3
+ "version": "0.1.1934",
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"
@@ -1,5 +0,0 @@
1
- {
2
- "runtime": "copilot",
3
- "models": null,
4
- "cachedAt": "2026-05-14T03:48:55.090Z"
5
- }