@yemi33/minions 0.1.634 → 0.1.636

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,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.636 (2026-04-08)
4
+
5
+ ### Fixes
6
+ - retry items bypass completed-dedup in isAlreadyDispatched
7
+
8
+ ### Other
9
+ - perf: extend caches, skip doc re-send on session resume
10
+
3
11
  ## 0.1.634 (2026-04-08)
4
12
 
5
13
  ### Fixes
package/dashboard.js CHANGED
@@ -548,7 +548,7 @@ const _ccPromptHash = require('crypto').createHash('md5').update(CC_STATIC_SYSTE
548
548
 
549
549
  let _preambleCache = null;
550
550
  let _preambleCacheTs = 0;
551
- const PREAMBLE_TTL = 10000; // 10ssame as status cache
551
+ const PREAMBLE_TTL = 30000; // 30slonger TTL since preamble is lightweight orientation, not real-time data
552
552
 
553
553
  function buildCCStatePreamble() {
554
554
  const now = Date.now();
@@ -676,6 +676,7 @@ function updateSession(store, key, sessionId, existing) {
676
676
  sessionId,
677
677
  lastActiveAt: now,
678
678
  turnCount: (existing && prev ? prev.turnCount : 0) + 1,
679
+ _docHash: prev?._docHash || null,
679
680
  });
680
681
  persistDocSessions();
681
682
  }
@@ -772,15 +773,32 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
772
773
 
773
774
  // Doc-specific wrapper — adds document context, parses ---DOCUMENT---
774
775
  async function ccDocCall({ message, document, title, filePath, selection, canEdit, isJson, model }) {
775
- const docContext = `## Document Context\n**${title || 'Document'}**${filePath ? ' (`' + filePath + '`)' : ''}${isJson ? ' (JSON)' : ''}\n${selection ? '\n**Selected text:**\n> ' + selection.slice(0, 1500) + '\n' : ''}\n\`\`\`\n${document.slice(0, 20000)}\n\`\`\`\n${canEdit ? '\nIf editing: respond with your explanation, then `---DOCUMENT---` on its own line, then the COMPLETE updated file.' : '\n(Read-only — answer questions only.)'}`;
776
-
777
- // Session key: filePath is stable and unique; title is the fallback for read-only Q&A
778
776
  const sessionKey = filePath || title;
777
+ const docSlice = document.slice(0, 20000);
778
+
779
+ // Skip re-sending full document on session resume if content unchanged
780
+ const docHash = require('crypto').createHash('md5').update(docSlice).digest('hex').slice(0, 8);
781
+ const existing = resolveSession('doc', sessionKey);
782
+ const docUnchanged = existing?.sessionId && existing._docHash === docHash;
783
+
784
+ let docContext;
785
+ if (docUnchanged) {
786
+ // Session has the document — only send selection and edit instructions
787
+ docContext = `## Document: ${title || 'Document'}${filePath ? ' (`' + filePath + '`)' : ''}${selection ? '\n**Selected text:**\n> ' + selection.slice(0, 1500) : ''}${canEdit ? '\nIf editing: respond with your explanation, then `---DOCUMENT---` on its own line, then the COMPLETE updated file.' : ''}`;
788
+ } else {
789
+ docContext = `## Document Context\n**${title || 'Document'}**${filePath ? ' (`' + filePath + '`)' : ''}${isJson ? ' (JSON)' : ''}\n${selection ? '\n**Selected text:**\n> ' + selection.slice(0, 1500) + '\n' : ''}\n\`\`\`\n${docSlice}\n\`\`\`\n${canEdit ? '\nIf editing: respond with your explanation, then `---DOCUMENT---` on its own line, then the COMPLETE updated file.' : '\n(Read-only — answer questions only.)'}`;
790
+ }
791
+
779
792
  const result = await ccCall(message, {
780
793
  store: 'doc', sessionKey,
781
794
  extraContext: docContext, label: 'doc-chat',
782
795
  ...(model ? { model } : {}),
783
796
  });
797
+ // Store doc hash for next call's unchanged check
798
+ if (result.code === 0 && result.sessionId) {
799
+ const session = resolveSession('doc', sessionKey);
800
+ if (session) session._docHash = docHash;
801
+ }
784
802
 
785
803
  if (result.code !== 0 || !result.text) {
786
804
  console.error(`[doc-chat] Failed: code=${result.code}, empty=${!result.text}, filePath=${filePath}, stderr=${(result.stderr || '').slice(0, 200)}`);
package/engine/llm.js CHANGED
@@ -50,7 +50,7 @@ function trackEngineUsage(category, usage) {
50
50
 
51
51
  let _claudeBinCache = null;
52
52
  let _claudeBinCacheTs = 0;
53
- const _CLAUDE_BIN_TTL = 300000; // 5 min — re-validate binary exists periodically
53
+ const _CLAUDE_BIN_TTL = 1800000; // 30 min — binary path rarely changes during a session
54
54
  function _resolveClaudeBin() {
55
55
  if (_claudeBinCache && Date.now() - _claudeBinCacheTs < _CLAUDE_BIN_TTL) return _claudeBinCache;
56
56
  _claudeBinCache = null;
package/engine.js CHANGED
@@ -1696,15 +1696,28 @@ function discoverFromWorkItems(config, project) {
1696
1696
  delete item._resumedAt;
1697
1697
  needsWrite = true;
1698
1698
  }
1699
+ // Skip dedup for items explicitly marked for retry (_retryCount set by engine)
1700
+ const isRetry = !!item._retryCount;
1699
1701
  if (isAlreadyDispatched(key)) {
1700
- // Only self-heal to DISPATCHED if actually in dispatch.active (agent spawned) (#480)
1701
- const existingActive = getDispatch().active?.find(d => d.meta?.dispatchKey === key);
1702
- if (existingActive) {
1703
- if (item.status === WI_STATUS.PENDING) { item.status = WI_STATUS.DISPATCHED; needsWrite = true; }
1704
- if (!item.dispatched_to && existingActive.agent) { item.dispatched_to = existingActive.agent; needsWrite = true; }
1702
+ // Retry items should bypass the completed-dedup but still block if in-flight
1703
+ if (isRetry) {
1704
+ const inFlight = [...(getDispatch().pending || []), ...(getDispatch().active || [])];
1705
+ if (!inFlight.some(d => d.meta?.dispatchKey === key)) {
1706
+ // Not in-flight allow retry to proceed
1707
+ } else {
1708
+ if (item._pendingReason !== 'already_dispatched') { item._pendingReason = 'already_dispatched'; needsWrite = true; }
1709
+ skipped.gated++; continue;
1710
+ }
1711
+ } else {
1712
+ // Only self-heal to DISPATCHED if actually in dispatch.active (agent spawned) (#480)
1713
+ const existingActive = getDispatch().active?.find(d => d.meta?.dispatchKey === key);
1714
+ if (existingActive) {
1715
+ if (item.status === WI_STATUS.PENDING) { item.status = WI_STATUS.DISPATCHED; needsWrite = true; }
1716
+ if (!item.dispatched_to && existingActive.agent) { item.dispatched_to = existingActive.agent; needsWrite = true; }
1717
+ }
1718
+ if (item._pendingReason !== 'already_dispatched') { item._pendingReason = 'already_dispatched'; needsWrite = true; }
1719
+ skipped.gated++; continue;
1705
1720
  }
1706
- if (item._pendingReason !== 'already_dispatched') { item._pendingReason = 'already_dispatched'; needsWrite = true; }
1707
- skipped.gated++; continue;
1708
1721
  }
1709
1722
  if (isOnCooldown(key, cooldownMs)) {
1710
1723
  if (item._pendingReason !== 'cooldown') { item._pendingReason = 'cooldown'; needsWrite = true; }
@@ -2130,7 +2143,8 @@ function discoverCentralWorkItems(config) {
2130
2143
 
2131
2144
  const key = `central-work-${item.id}`;
2132
2145
  // Self-heal: if already dispatched but work item is still pending, fix the status
2133
- if (isAlreadyDispatched(key)) {
2146
+ // Skip dedup for items explicitly marked for retry (_retryCount set by engine)
2147
+ if (!item._retryCount && isAlreadyDispatched(key)) {
2134
2148
  // Only self-heal to DISPATCHED if actually in dispatch.active (agent spawned) (#480)
2135
2149
  const existingActive = getDispatch().active?.find(d => d.meta?.dispatchKey === key);
2136
2150
  if (existingActive) {
@@ -2141,6 +2155,11 @@ function discoverCentralWorkItems(config) {
2141
2155
  }
2142
2156
  continue;
2143
2157
  }
2158
+ // Still block if actively in flight (pending or active dispatch)
2159
+ if (item._retryCount && isAlreadyDispatched(key)) {
2160
+ const inFlight = [...(getDispatch().pending || []), ...(getDispatch().active || [])];
2161
+ if (inFlight.some(d => d.meta?.dispatchKey === key)) continue;
2162
+ }
2144
2163
  if (isOnCooldown(key, 0)) continue;
2145
2164
 
2146
2165
  const workType = item.type || 'implement';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.634",
3
+ "version": "0.1.636",
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"