@yemi33/minions 0.1.1990 → 0.1.1991

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/dashboard.js CHANGED
@@ -34,7 +34,7 @@ const features = require('./engine/features');
34
34
  const ccWorkerPool = require('./engine/cc-worker-pool');
35
35
  const os = require('os');
36
36
 
37
- const { safeRead, safeReadDir, safeWrite, safeJson, safeJsonObj, safeJsonArr, safeJsonNoRestore, safeUnlink, mutateJsonFileLocked, mutateTextFileLocked, mutateControl, mutateCooldowns, mutateWorkItems, getProjects: _getProjects, DONE_STATUSES, WI_STATUS, WORK_TYPE, WORKTREE_REQUIRING_TYPES, reopenWorkItem } = shared;
37
+ const { safeRead, safeReadOrNull, safeReadDir, safeWrite, safeJson, safeJsonObj, safeJsonArr, safeJsonNoRestore, safeUnlink, mutateJsonFileLocked, mutateTextFileLocked, mutateControl, mutateCooldowns, mutateWorkItems, getProjects: _getProjects, DONE_STATUSES, WI_STATUS, WORK_TYPE, WORKTREE_REQUIRING_TYPES, reopenWorkItem } = shared;
38
38
  const { getAgents, getAgentDetail, getPrdInfo, getWorkItems, getDispatchQueue,
39
39
  getSkills, getInbox, getNotesWithMeta, getPullRequests,
40
40
  getEngineLog, getMetrics, getKnowledgeBaseEntries, getProjectGitStatus, timeSince,
@@ -2640,11 +2640,31 @@ async function _preflightModelCheck({ runtime: cliOverride, model: modelOverride
2640
2640
  * document body. Always re-sending extraContext is correctness-safe; the
2641
2641
  * pool's warm-process saving is preserved regardless.
2642
2642
  */
2643
+ /**
2644
+ * Build the persistent doc-chat pool tabKey from a session key (filePath or
2645
+ * title). Single source of truth shared by _invokeDocChatViaPool (the
2646
+ * streaming path) and handleDocChatWarm (the pre-warm endpoint). Both call
2647
+ * sites MUST resolve to the same key, otherwise warm spawns an orphan worker
2648
+ * the streaming call ignores — silently breaking the skip-18-21s Copilot
2649
+ * cold-spawn optimization documented at modal-qa.js:506-516.
2650
+ *
2651
+ * The `default` fallback mirrors the legacy inline behavior for empty input
2652
+ * so older callers (and the dashboard test fixtures that omit sessionKey)
2653
+ * still land on a stable, predictable key.
2654
+ *
2655
+ * The freshSession one-shot branch in _invokeDocChatViaPool intentionally
2656
+ * bypasses this helper — those callers want a unique short-lived key under
2657
+ * `doc-chat:fresh:<uid>`, not the persistent namespace.
2658
+ */
2659
+ function _buildDocChatTabKey(sessionKey) {
2660
+ return 'doc-chat:' + (sessionKey || 'default');
2661
+ }
2662
+
2643
2663
  function _invokeDocChatViaPool({ prompt, model, effort, engineConfig, systemPrompt, sessionKey, freshSession, timeoutMs, onChunk, onToolUse, onToolUpdate }) {
2644
2664
  const oneShot = !!freshSession;
2645
2665
  const tabKey = oneShot
2646
2666
  ? 'doc-chat:fresh:' + shared.uid()
2647
- : 'doc-chat:' + (sessionKey || 'default');
2667
+ : _buildDocChatTabKey(sessionKey);
2648
2668
  let cancelled = false;
2649
2669
  let settled = false;
2650
2670
  let accumulated = '';
@@ -5905,7 +5925,10 @@ What would you like to discuss or change? When you're happy, say "approve" and I
5905
5925
  if (canEdit) {
5906
5926
  try { shared.sanitizePath(body.filePath, MINIONS_DIR); } catch { return jsonReply(res, 400, { error: 'path must be under minions directory' }); }
5907
5927
  fullPath = path.resolve(MINIONS_DIR, body.filePath);
5908
- const diskContent = safeRead(fullPath);
5928
+ // safeReadOrNull returns null when the file is missing/inaccessible.
5929
+ // If absent, keep body.document (the client-sent panel content) — do
5930
+ // NOT overwrite currentContent with '' (W-mpede4j7000ab7e4).
5931
+ const diskContent = safeReadOrNull(fullPath);
5909
5932
  if (diskContent !== null) {
5910
5933
  // If client sent a contentHash and it matches disk, skip replacement — client copy is fresh
5911
5934
  if (body.contentHash && contentFingerprint(diskContent) === body.contentHash) {
@@ -5992,7 +6015,10 @@ What would you like to discuss or change? When you're happy, say "approve" and I
5992
6015
  try { shared.sanitizePath(body.filePath, MINIONS_DIR); }
5993
6016
  catch { docChatInFlight.delete(docKey); res.statusCode = 400; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ error: 'path must be under minions directory' })); return; }
5994
6017
  fullPath = path.resolve(MINIONS_DIR, body.filePath);
5995
- const diskContent = safeRead(fullPath);
6018
+ // safeReadOrNull returns null when the file is missing/inaccessible.
6019
+ // If absent, keep body.document (the client-sent panel content) — do
6020
+ // NOT overwrite currentContent with '' (W-mpede4j7000ab7e4).
6021
+ const diskContent = safeReadOrNull(fullPath);
5996
6022
  if (diskContent !== null) {
5997
6023
  if (!(body.contentHash && contentFingerprint(diskContent) === body.contentHash)) {
5998
6024
  currentContent = diskContent;
@@ -6595,7 +6621,10 @@ What would you like to discuss or change? When you're happy, say "approve" and I
6595
6621
  const body = await readBody(req);
6596
6622
  const sessionKey = body && (body.filePath || body.title);
6597
6623
  if (!sessionKey) return jsonReply(res, 400, { error: 'filePath or title required' });
6598
- const result = await _warmCcPool(sessionKey, _docChatPromptHash);
6624
+ // Must match the persistent tabKey _invokeDocChatViaPool hands to
6625
+ // ccWorkerPool.getSession at first-message time, or this warm spawns
6626
+ // an orphan worker (regression W-mpede4bf000957b5).
6627
+ const result = await _warmCcPool(_buildDocChatTabKey(sessionKey), _docChatPromptHash);
6599
6628
  return jsonReply(res, 200, result);
6600
6629
  } catch (e) { return jsonReply(res, e.statusCode || 500, { error: e.message }); }
6601
6630
  }
@@ -9261,6 +9290,7 @@ module.exports = {
9261
9290
  parsePinnedEntries,
9262
9291
  _formatDocChatContext,
9263
9292
  _buildDocChatPass,
9293
+ _buildDocChatTabKey,
9264
9294
  _docSessionsForTesting: docSessions,
9265
9295
  _docChatPromptHashForTesting: _docChatPromptHash,
9266
9296
  _isCompletedMeetingJson,
package/engine/shared.js CHANGED
@@ -323,6 +323,15 @@ function safeRead(p) {
323
323
  try { return fs.readFileSync(p, 'utf8'); } catch { return ''; }
324
324
  }
325
325
 
326
+ // Like safeRead, but returns null when the file is missing/inaccessible so
327
+ // callers can distinguish "file absent" from "file present but empty".
328
+ // Used by doc-chat handlers (W-mpede4j7000ab7e4) to avoid silently overwriting
329
+ // the client-sent document body with '' when the file was deleted/moved
330
+ // between panel open and send.
331
+ function safeReadOrNull(p) {
332
+ try { return fs.readFileSync(p, 'utf8'); } catch { return null; }
333
+ }
334
+
326
335
  function safeReadDir(dir) {
327
336
  try { return fs.readdirSync(dir); } catch { return []; }
328
337
  }
@@ -4680,6 +4689,7 @@ module.exports = {
4680
4689
  dateStamp,
4681
4690
  log,
4682
4691
  safeRead,
4692
+ safeReadOrNull,
4683
4693
  safeReadDir,
4684
4694
  safeJson, safeJsonObj, safeJsonArr, safeJsonNoRestore,
4685
4695
  safeWrite,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1990",
3
+ "version": "0.1.1991",
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"