@yemi33/minions 0.1.1773 → 0.1.1774

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,16 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1773 (2026-05-07)
3
+ ## 0.1.1774 (2026-05-07)
4
+
5
+ ### Features
6
+ - suppress stale doc-chat model errors
7
+
8
+ ### Fixes
9
+ - yemi33/minions#2168
10
+
11
+ ## 0.1.1772 (2026-05-07)
4
12
 
5
13
  ### Features
6
- - fix doc chat sticky scroll (#2176)
7
- - fix live output route and CLI docs (#2172)
8
- - make API project resolution strict (#2169)
9
14
  - fix copilot cc resume context (#2166)
10
15
 
11
16
  ## 0.1.1771 (2026-05-07)
package/dashboard.js CHANGED
@@ -2721,23 +2721,19 @@ function _formatDocChatContext({ document, title, filePath, selection, canEdit,
2721
2721
  return context;
2722
2722
  }
2723
2723
 
2724
- // Map errorClass codes from the runtime adapter to actionable user-facing messages.
2724
+ // Map runtime failures to user-facing messages.
2725
2725
  // sessionPreserved=true means ccCall preserved the session — user can retry immediately.
2726
2726
  // toolUses=[] from result.toolUses lets the message warn that tools may have already
2727
2727
  // modified files/state before the failure — the user shouldn't assume nothing happened.
2728
- // errorMessage is the runtime adapter's own remediation string (from parseError)
2729
- // authoritative because it knows whether the runtime is Claude, Copilot, or another;
2730
- // dashboard falls back to generic copy only when the adapter didn't supply one.
2728
+ // errorMessage is the runtime adapter's own text from parseError; when present it
2729
+ // is authoritative and is shown directly rather than replaced with dashboard copy.
2731
2730
  function _docChatErrorMessage(errorClass, sessionPreserved = false, toolUses = [], errorMessage = null) {
2732
2731
  const tools = Array.isArray(toolUses) ? toolUses : [];
2733
2732
  const toolHint = tools.length > 0
2734
2733
  ? ` (${tools.length} tool${tools.length === 1 ? '' : 's'} ran before the failure: ${tools.slice(0, 5).map(t => t.name).join(', ')}${tools.length > 5 ? '…' : ''} — files or state may have been modified.)`
2735
2734
  : '';
2736
- if (errorClass === 'auth-failure') return (errorMessage || 'Runtime authentication failed — check your CLI auth or API key, then try again.') + toolHint;
2737
- if (errorClass === 'context-limit') return 'Session context is too long. Click "Clear" to start a fresh conversation.' + toolHint;
2738
- if (errorClass === 'budget-exceeded') return (errorMessage || 'Runtime budget exceeded — check your account or quota.') + toolHint;
2739
- if (errorClass === 'crash') return (errorMessage || 'Runtime crashed unexpectedly. Try again.') + toolHint;
2740
- if (errorClass === 'unknown-model') return (errorMessage || 'Configured model is not valid for the active runtime. Update engine.ccModel or engine.defaultModel.') + toolHint;
2735
+ const directError = typeof errorMessage === 'string' ? errorMessage.trim() : '';
2736
+ if (directError) return directError + toolHint;
2741
2737
  if (sessionPreserved) return 'Temporary connection issue — your conversation is intact, send your message again.' + toolHint;
2742
2738
  if (tools.length > 0) return 'The agent stopped responding before producing a final answer.' + toolHint;
2743
2739
  return 'Failed to process request. Try again.';
@@ -2746,14 +2742,26 @@ function _docChatErrorMessage(errorClass, sessionPreserved = false, toolUses = [
2746
2742
  // Secondary note rendered alongside a recovered partial answer — distinct from the
2747
2743
  // hard-failure message because the answer/actions/document-edit DID land. The user
2748
2744
  // just needs to know the run wasn't clean.
2749
- function _docChatPartialWarning(errorClass) {
2750
- if (errorClass === 'auth-failure') return 'Note: auth failed — answer recovered from partial output, but follow-up turns may not work.';
2751
- if (errorClass === 'context-limit') return 'Note: session context was too long — answer recovered. Click "Clear" before continuing.';
2752
- if (errorClass === 'budget-exceeded') return 'Note: runtime budget exceeded — answer recovered, but further calls may fail.';
2753
- if (errorClass === 'crash') return 'Note: runtime crashed before clean exit, but a complete response was recovered.';
2745
+ function _docChatPartialWarning(errorClass, errorMessage = null) {
2746
+ const directError = typeof errorMessage === 'string' ? errorMessage.trim() : '';
2747
+ if (directError) return `Note: ${directError}`;
2754
2748
  return 'Note: the agent exited unexpectedly. A complete response was recovered — verify any saved files or dispatched actions.';
2755
2749
  }
2756
2750
 
2751
+ function _docChatResultHasVisibleError(result) {
2752
+ if (!result) return false;
2753
+ if (result.errorClass) return true;
2754
+ if (typeof result.errorMessage === 'string' && result.errorMessage.trim()) return true;
2755
+ // stderr without a classified/runtime error is often CLI diagnostics; hard
2756
+ // failures with no answer still surface stderr through _docChatFailureResponse.
2757
+ return false;
2758
+ }
2759
+
2760
+ function _docChatResultLooksSuccessful(result) {
2761
+ if (!result || !result.text) return false;
2762
+ return result.code === 0 || !_docChatResultHasVisibleError(result);
2763
+ }
2764
+
2757
2765
  // Build the doc-chat extraContext for a single ccCall pass — refreshed on retry
2758
2766
  // so a fresh-session retry includes the full document instead of relying on the
2759
2767
  // dead session's prior turn for context.
@@ -2830,7 +2838,7 @@ function _recoverPartialDocChatResponse(result, sessionKey) {
2830
2838
  return {
2831
2839
  ...parsed,
2832
2840
  partial: true,
2833
- warning: _docChatPartialWarning(result.errorClass),
2841
+ warning: _docChatPartialWarning(result.errorClass, result.errorMessage || result.stderr || null),
2834
2842
  toolUses: Array.isArray(result.toolUses) ? result.toolUses : [],
2835
2843
  // Recovery path still attaches the raw runtime failure — the answer landed
2836
2844
  // despite a non-zero exit; users still benefit from seeing why.
@@ -2838,6 +2846,41 @@ function _recoverPartialDocChatResponse(result, sessionKey) {
2838
2846
  };
2839
2847
  }
2840
2848
 
2849
+ function _shouldSuppressDocChatPostPatchError(ccError, finalize) {
2850
+ if (!finalize || finalize.edited !== true) return false;
2851
+ if (!ccError || ccError.errorClass !== 'unknown-model') return false;
2852
+ return String(ccError.runtime || '').toLowerCase() === 'copilot';
2853
+ }
2854
+
2855
+ function _buildDocChatResponsePayload({
2856
+ answer,
2857
+ actions,
2858
+ actionResults,
2859
+ actionParseError,
2860
+ ccError,
2861
+ partial,
2862
+ warning,
2863
+ toolUses,
2864
+ finalize,
2865
+ } = {}) {
2866
+ const final = finalize || { edited: false, content: null, answerSuffix: '' };
2867
+ const suppressPostPatchError = _shouldSuppressDocChatPostPatchError(ccError, final);
2868
+ const visibleAnswer = suppressPostPatchError && !partial ? 'Updated the document.' : answer;
2869
+ const finalAnswer = final.answerSuffix ? visibleAnswer + final.answerSuffix : visibleAnswer;
2870
+ return {
2871
+ ok: !(ccError && !suppressPostPatchError),
2872
+ answer: finalAnswer,
2873
+ actions,
2874
+ ...(actionResults ? { actionResults } : {}),
2875
+ ...(actionParseError ? { actionParseError } : {}),
2876
+ ...(ccError && !suppressPostPatchError ? { error: ccError } : {}),
2877
+ ...(partial && !suppressPostPatchError ? { partial: true, warning } : {}),
2878
+ ...(Array.isArray(toolUses) && toolUses.length ? { toolUses } : {}),
2879
+ edited: final.edited,
2880
+ ...(final.edited && final.content !== null ? { content: final.content } : {}),
2881
+ };
2882
+ }
2883
+
2841
2884
 
2842
2885
  // True when the file is a meeting JSON whose status forbids edits. Loaded
2843
2886
  // fresh on each call because meeting status can change while a doc-chat is
@@ -2990,7 +3033,7 @@ async function ccDocCall({ message, document, title, filePath, selection, canEdi
2990
3033
  // bleed into future interactions under the same key.
2991
3034
  docSessions.delete(sessionKey);
2992
3035
  schedulePersistDocSessions();
2993
- } else if (result.code === 0 && result.sessionId) {
3036
+ } else if (_docChatResultLooksSuccessful(result) && result.sessionId) {
2994
3037
  // Store doc hash for next call's unchanged check
2995
3038
  const session = resolveSession('doc', sessionKey);
2996
3039
  if (session) session._docHash = initialPass.docHash;
@@ -3000,7 +3043,7 @@ async function ccDocCall({ message, document, title, filePath, selection, canEdi
3000
3043
  return { answer: result.text || result.stderr || 'Minions runtime is not installed or configured.', content: null, actions: [] };
3001
3044
  }
3002
3045
 
3003
- if (result.code !== 0 || !result.text) {
3046
+ if (!_docChatResultLooksSuccessful(result)) {
3004
3047
  // Try to salvage a parseable answer / action / document edit before failing.
3005
3048
  const recovered = _recoverPartialDocChatResponse(result, sessionKey);
3006
3049
  if (recovered) return recovered;
@@ -3051,7 +3094,7 @@ async function ccDocCallStreaming({ message, document, title, filePath, selectio
3051
3094
  if (freshSession && sessionKey) {
3052
3095
  docSessions.delete(sessionKey);
3053
3096
  schedulePersistDocSessions();
3054
- } else if (result.code === 0 && result.sessionId) {
3097
+ } else if (_docChatResultLooksSuccessful(result) && result.sessionId) {
3055
3098
  const session = resolveSession('doc', sessionKey);
3056
3099
  if (session) session._docHash = initialPass.docHash;
3057
3100
  }
@@ -3060,7 +3103,7 @@ async function ccDocCallStreaming({ message, document, title, filePath, selectio
3060
3103
  return { answer: result.text || result.stderr || 'Minions runtime is not installed or configured.', content: null, actions: [] };
3061
3104
  }
3062
3105
 
3063
- if (result.code !== 0 || !result.text) {
3106
+ if (!_docChatResultLooksSuccessful(result)) {
3064
3107
  const recovered = _recoverPartialDocChatResponse(result, sessionKey);
3065
3108
  if (recovered) return recovered;
3066
3109
  const sessionPreserved = !!(resolveSession('doc', sessionKey)?.sessionId);
@@ -5201,20 +5244,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
5201
5244
  filePath: body.filePath, fullPath, isJson, canEdit,
5202
5245
  originalContent: currentContent, delimiterContent: content,
5203
5246
  });
5204
- const finalAnswer = finalize.answerSuffix ? answer + finalize.answerSuffix : answer;
5205
- _docDone = true;
5206
- return jsonReply(res, 200, {
5207
- ok: !ccError,
5208
- answer: finalAnswer,
5209
- actions,
5210
- ...(actionResults ? { actionResults } : {}),
5211
- ...(actionParseError ? { actionParseError } : {}),
5212
- ...(ccError ? { error: ccError } : {}),
5213
- ...(partial ? { partial: true, warning } : {}),
5214
- ...(Array.isArray(toolUses) && toolUses.length ? { toolUses } : {}),
5215
- edited: finalize.edited,
5216
- ...(finalize.edited && finalize.content !== null ? { content: finalize.content } : {}),
5247
+ const payload = _buildDocChatResponsePayload({
5248
+ answer, actions, actionResults, actionParseError, ccError, partial, warning, toolUses, finalize,
5217
5249
  });
5250
+ _docDone = true;
5251
+ return jsonReply(res, 200, payload);
5218
5252
  } finally { _docAbort = null; _docDone = true; docChatInFlight.delete(docKey); }
5219
5253
  } catch (e) { return jsonReply(res, e.statusCode || 500, { error: e.message }); }
5220
5254
  }
@@ -5303,18 +5337,14 @@ What would you like to discuss or change? When you're happy, say "approve" and I
5303
5337
  filePath: body.filePath, fullPath, isJson, canEdit,
5304
5338
  originalContent: currentContent, delimiterContent: content,
5305
5339
  });
5306
- const finalAnswer = finalize.answerSuffix ? answer + finalize.answerSuffix : answer;
5340
+ const payload = _buildDocChatResponsePayload({
5341
+ answer, actions, actionResults, actionParseError, ccError, partial, warning, toolUses, finalize,
5342
+ });
5343
+ const { answer: finalAnswer, ...donePayload } = payload;
5307
5344
  writeDocEvent({
5308
5345
  type: 'done',
5309
5346
  text: finalAnswer,
5310
- actions,
5311
- ...(actionResults ? { actionResults } : {}),
5312
- ...(actionParseError ? { actionParseError } : {}),
5313
- ...(ccError ? { error: ccError } : {}),
5314
- ...(partial ? { partial: true, warning } : {}),
5315
- ...(Array.isArray(toolUses) && toolUses.length ? { toolUses } : {}),
5316
- edited: finalize.edited,
5317
- ...(finalize.edited && finalize.content !== null ? { content: finalize.content } : {}),
5347
+ ...donePayload,
5318
5348
  });
5319
5349
  _docStreamEnded = true;
5320
5350
  res.end();
@@ -7753,6 +7783,10 @@ module.exports = {
7753
7783
  _docChatPartialWarning,
7754
7784
  _docChatFailureResponse,
7755
7785
  _recoverPartialDocChatResponse,
7786
+ _docChatResultHasVisibleError,
7787
+ _docChatResultLooksSuccessful,
7788
+ _shouldSuppressDocChatPostPatchError,
7789
+ _buildDocChatResponsePayload,
7756
7790
  _linkPullRequestForTracking: linkPullRequestForTracking,
7757
7791
  _resolveSkillReadPath,
7758
7792
  DOC_CHAT_DOCUMENT_DELIMITER,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "runtime": "copilot",
3
3
  "models": null,
4
- "cachedAt": "2026-05-07T17:23:11.001Z"
4
+ "cachedAt": "2026-05-07T21:34:32.453Z"
5
5
  }
@@ -634,25 +634,77 @@ function parseStreamChunk(line) {
634
634
 
635
635
  // ── Error Normalization ─────────────────────────────────────────────────────
636
636
 
637
- function parseError(rawOutput) {
637
+ function _collectErrorSignal(rawOutput) {
638
638
  const text = rawOutput == null ? '' : String(rawOutput);
639
+ if (!text) return '';
640
+
641
+ const signals = [];
642
+ let sawJsonLine = false;
643
+ for (const rawLine of text.split('\n')) {
644
+ const line = rawLine.trim();
645
+ if (!line) continue;
646
+ if (!line.startsWith('{')) {
647
+ signals.push(line);
648
+ continue;
649
+ }
650
+
651
+ let obj;
652
+ try { obj = JSON.parse(line); } catch { continue; }
653
+ if (!obj || typeof obj !== 'object') continue;
654
+ sawJsonLine = true;
655
+
656
+ const type = String(obj.type || '');
657
+ const subtype = String(obj.subtype || '');
658
+ const eventType = String(obj.event?.type || '');
659
+ const isErrorEvent = type === 'error'
660
+ || eventType === 'error'
661
+ || subtype.startsWith('error')
662
+ || obj.is_error === true
663
+ || obj.error != null
664
+ || obj.data?.error != null;
665
+ if (!isErrorEvent) continue;
666
+
667
+ for (const value of [
668
+ obj.error,
669
+ obj.message,
670
+ obj.stderr,
671
+ obj.result,
672
+ obj.data?.error,
673
+ obj.data?.message,
674
+ obj.data?.stderr,
675
+ obj.event?.error,
676
+ obj.event?.message,
677
+ subtype,
678
+ ]) {
679
+ if (typeof value === 'string' && value.trim()) signals.push(value.trim());
680
+ }
681
+ }
682
+
683
+ if (signals.length > 0) return signals.join('\n');
684
+ return sawJsonLine ? '' : text;
685
+ }
686
+
687
+ function parseError(rawOutput) {
688
+ const text = _collectErrorSignal(rawOutput);
639
689
  if (!text) return { message: '', code: null, retriable: true };
640
690
  const lower = text.toLowerCase();
641
691
 
642
- if (/not authenticated|copilot login|please.*log.*in|401|403 forbidden|unauthorized/i.test(text)) {
643
- return { message: 'Copilot/GitHub authentication failed. Run `gh auth login` or provide GH_TOKEN/COPILOT_GITHUB_TOKEN with Copilot access.', code: 'auth-failure', retriable: false };
692
+ const hasExplicitAuthFailure = /not authenticated|copilot login|please.*log.*in|\bunauthorized\b/i.test(text);
693
+ const hasAuthStatusCode = /\b(?:http(?:\/\d(?:\.\d)?)?|status(?:\s+code)?|statuscode|response(?:\s+status)?|api(?:\s+(?:error|response|status))?)\s*[:=]?\s*(?:401|403)\b|\b(?:401\s+unauthorized|403\s+forbidden)\b/i.test(text);
694
+ if (hasExplicitAuthFailure || hasAuthStatusCode) {
695
+ return { message: text, code: 'auth-failure', retriable: false };
644
696
  }
645
697
  if (/rate limit|too many requests|\b429\b/i.test(text)) {
646
- return { message: 'Copilot rate limit hit', code: 'rate-limit', retriable: true };
698
+ return { message: text, code: 'rate-limit', retriable: true };
647
699
  }
648
700
  if (/unknown model|model not found|model.*invalid|invalid model/i.test(text)) {
649
- return { message: 'Copilot rejected the requested model', code: 'unknown-model', retriable: false };
701
+ return { message: text, code: 'unknown-model', retriable: false };
650
702
  }
651
703
  if (/budget.*exceed|premium.*limit.*reach|quota.*exceed/i.test(lower)) {
652
- return { message: 'Copilot premium-request budget exceeded — check your GitHub Copilot quota.', code: 'budget-exceeded', retriable: false };
704
+ return { message: text, code: 'budget-exceeded', retriable: false };
653
705
  }
654
706
  if (/internal error|panic|uncaught|copilot.*crashed|fatal: copilot/i.test(lower)) {
655
- return { message: 'Copilot CLI crashed unexpectedly. Try again.', code: 'crash', retriable: true };
707
+ return { message: text, code: 'crash', retriable: true };
656
708
  }
657
709
  return { message: '', code: null, retriable: true };
658
710
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1773",
3
+ "version": "0.1.1774",
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"