@link-assistant/hive-mind 1.9.1 → 1.10.0

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,32 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9b56b26: feat(solve): configure MCP_TIMEOUT and MCP_TOOL_TIMEOUT for claude tool calls
8
+
9
+ Added MCP timeout configuration to prevent tool calls from hanging indefinitely:
10
+ - Added `mcpTimeout` config (default: 900000ms / 15 minutes) for MCP server startup
11
+ - Added `mcpToolTimeout` config (default: 900000ms / 15 minutes) for MCP tool execution
12
+ - Support for override via `MCP_TIMEOUT`/`HIVE_MIND_MCP_TIMEOUT` and `MCP_TOOL_TIMEOUT`/`HIVE_MIND_MCP_TOOL_TIMEOUT` environment variables
13
+ - Updated `getClaudeEnv()` to pass both timeout values to Claude CLI
14
+ - Added verbose logging for MCP timeout values
15
+
16
+ Fixes #1066
17
+
18
+ ## 1.9.2
19
+
20
+ ### Patch Changes
21
+
22
+ - d39bf3e: Fix disk threshold to use one-at-a-time mode instead of blocking all commands
23
+ - When disk usage exceeds threshold (90%), now allows exactly one command to run
24
+ - Previously, disk threshold blocked ALL commands unconditionally (like RAM/CPU)
25
+ - Now matches behavior of Claude API thresholds (CLAUDE_5_HOUR_SESSION_THRESHOLD, CLAUDE_WEEKLY_THRESHOLD)
26
+ - Allows controlled task execution during high disk usage while preventing multiple tasks from exhausting resources
27
+
28
+ Fixes #1155
29
+
3
30
  ## 1.9.1
4
31
 
5
32
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.9.1",
3
+ "version": "1.10.0",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -881,9 +881,12 @@ export const executeClaudeCommand = async params => {
881
881
  // See issue #1146 for details on thinking budget translation
882
882
  const { thinkingBudget: resolvedThinkingBudget, thinkLevel, isNewVersion } = await resolveThinkingSettings(argv, log);
883
883
 
884
- // Set CLAUDE_CODE_MAX_OUTPUT_TOKENS (see issue #1076) and optionally MAX_THINKING_TOKENS (see issue #1146)
884
+ // Set CLAUDE_CODE_MAX_OUTPUT_TOKENS (see issue #1076), MAX_THINKING_TOKENS (see issue #1146),
885
+ // and MCP timeout configurations (see issue #1066)
885
886
  const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget });
886
887
  if (argv.verbose) await log(`📊 CLAUDE_CODE_MAX_OUTPUT_TOKENS: ${claudeCode.maxOutputTokens}`, { verbose: true });
888
+ if (argv.verbose) await log(`📊 MCP_TIMEOUT: ${claudeCode.mcpTimeout}ms (server startup)`, { verbose: true });
889
+ if (argv.verbose) await log(`📊 MCP_TOOL_TIMEOUT: ${claudeCode.mcpToolTimeout}ms (tool execution)`, { verbose: true });
887
890
  if (resolvedThinkingBudget !== undefined) {
888
891
  await log(`📊 MAX_THINKING_TOKENS: ${resolvedThinkingBudget}`, { verbose: true });
889
892
  }
@@ -90,6 +90,15 @@ export const claudeCode = {
90
90
  // Default: 64000 (matches Claude Sonnet/Opus/Haiku 4.5 model capabilities)
91
91
  // Set via CLAUDE_CODE_MAX_OUTPUT_TOKENS or HIVE_MIND_CLAUDE_CODE_MAX_OUTPUT_TOKENS
92
92
  maxOutputTokens: parseIntWithDefault('CLAUDE_CODE_MAX_OUTPUT_TOKENS', parseIntWithDefault('HIVE_MIND_CLAUDE_CODE_MAX_OUTPUT_TOKENS', 64000)),
93
+ // MCP (Model Context Protocol) timeout configurations
94
+ // See: https://github.com/link-assistant/hive-mind/issues/1066
95
+ // See: https://code.claude.com/docs/en/settings#environment-variables
96
+ // MCP_TIMEOUT: Timeout in milliseconds for MCP server startup
97
+ // MCP_TOOL_TIMEOUT: Timeout in milliseconds for MCP tool execution
98
+ // Default: 900000ms (15 minutes) to accommodate long-running Playwright operations
99
+ // Set via MCP_TIMEOUT/MCP_TOOL_TIMEOUT or HIVE_MIND_MCP_TIMEOUT/HIVE_MIND_MCP_TOOL_TIMEOUT
100
+ mcpTimeout: parseIntWithDefault('MCP_TIMEOUT', parseIntWithDefault('HIVE_MIND_MCP_TIMEOUT', 900000)),
101
+ mcpToolTimeout: parseIntWithDefault('MCP_TOOL_TIMEOUT', parseIntWithDefault('HIVE_MIND_MCP_TOOL_TIMEOUT', 900000)),
93
102
  };
94
103
 
95
104
  // Default max thinking budget for Claude Code (see issue #1146)
@@ -156,8 +165,18 @@ export const supportsThinkingBudget = (version, minVersion = '2.1.12') => {
156
165
 
157
166
  // Helper function to get Claude CLI environment with CLAUDE_CODE_MAX_OUTPUT_TOKENS set
158
167
  // Optionally sets MAX_THINKING_TOKENS when thinkingBudget is provided (see issue #1146)
168
+ // Also sets MCP_TIMEOUT and MCP_TOOL_TIMEOUT for MCP tool execution (see issue #1066)
159
169
  export const getClaudeEnv = (options = {}) => {
160
- const env = { ...process.env, CLAUDE_CODE_MAX_OUTPUT_TOKENS: String(claudeCode.maxOutputTokens) };
170
+ const env = {
171
+ ...process.env,
172
+ CLAUDE_CODE_MAX_OUTPUT_TOKENS: String(claudeCode.maxOutputTokens),
173
+ // MCP timeout configurations to prevent tool calls from hanging indefinitely
174
+ // See: https://github.com/link-assistant/hive-mind/issues/1066
175
+ // MCP_TIMEOUT: Timeout for MCP server startup
176
+ // MCP_TOOL_TIMEOUT: Timeout for MCP tool execution (the one that prevents stuck tools)
177
+ MCP_TIMEOUT: String(claudeCode.mcpTimeout),
178
+ MCP_TOOL_TIMEOUT: String(claudeCode.mcpToolTimeout),
179
+ };
161
180
  // Set MAX_THINKING_TOKENS if thinkingBudget is provided
162
181
  // This controls Claude Code's extended thinking feature (Claude Code >= 2.1.12)
163
182
  // Default is 31999, set to 0 to disable thinking, max is 63999 for 64K output models
@@ -34,16 +34,16 @@ import { getCachedClaudeLimits, getCachedGitHubLimits, getCachedMemoryInfo, getC
34
34
  export const QUEUE_CONFIG = {
35
35
  // Resource thresholds (usage ratios: 0.0 - 1.0)
36
36
  // All thresholds use >= comparison (inclusive)
37
- RAM_THRESHOLD: 0.65, // Stop if RAM usage >= 65%
37
+ RAM_THRESHOLD: 0.65, // Enqueue if RAM usage >= 65%
38
38
  // CPU threshold uses 5-minute load average, not instantaneous CPU usage
39
- CPU_THRESHOLD: 0.75, // Stop if 5-minute load average >= 75% of CPU count
40
- DISK_THRESHOLD: 0.95, // One-at-a-time if disk usage >= 95%
39
+ CPU_THRESHOLD: 0.65, // Enqueue if 5-minute load average >= 65% of CPU count
40
+ DISK_THRESHOLD: 0.9, // One-at-a-time if disk usage >= 90%
41
41
 
42
42
  // API limit thresholds (usage ratios: 0.0 - 1.0)
43
43
  // All thresholds use >= comparison (inclusive)
44
- CLAUDE_5_HOUR_SESSION_THRESHOLD: 0.85, // Stop if 5-hour limit >= 85%
44
+ CLAUDE_5_HOUR_SESSION_THRESHOLD: 0.85, // One-at-a-time if 5-hour limit >= 85%
45
45
  CLAUDE_WEEKLY_THRESHOLD: 0.98, // One-at-a-time if weekly limit >= 98%
46
- GITHUB_API_THRESHOLD: 0.8, // Stop if GitHub >= 80% with parallel claude
46
+ GITHUB_API_THRESHOLD: 0.8, // Enqueue if GitHub >= 80% with parallel claude
47
47
 
48
48
  // Timing
49
49
  // MIN_START_INTERVAL_MS: Time to allow solve command to start actual claude process
@@ -431,11 +431,15 @@ export class SolveQueue {
431
431
  this.recordThrottle('claude_running');
432
432
  }
433
433
 
434
- // Check system resources (ultimate restrictions - block unconditionally)
435
- const resourceCheck = await this.checkSystemResources();
434
+ // Check system resources (RAM, CPU block unconditionally; disk uses one-at-a-time mode)
435
+ // See: https://github.com/link-assistant/hive-mind/issues/1155
436
+ const resourceCheck = await this.checkSystemResources(totalProcessing);
436
437
  if (!resourceCheck.ok) {
437
438
  reasons.push(...resourceCheck.reasons);
438
439
  }
440
+ if (resourceCheck.oneAtATime) {
441
+ oneAtATime = true;
442
+ }
439
443
 
440
444
  // Check API limits (pass hasRunningClaude and totalProcessing for uniform checking)
441
445
  const limitCheck = await this.checkApiLimits(hasRunningClaude, totalProcessing);
@@ -478,17 +482,22 @@ export class SolveQueue {
478
482
  * This provides a more stable metric that isn't affected by brief spikes
479
483
  * during claude process startup.
480
484
  *
481
- * Note: System resource thresholds are ultimate restrictions - they block unconditionally
482
- * when triggered. Unlike Claude API thresholds which allow one command at a time via
483
- * totalProcessing check, system resources block all new commands immediately.
484
- * See: https://github.com/link-assistant/hive-mind/issues/1133
485
+ * Resource threshold modes:
486
+ * - RAM_THRESHOLD: Enqueue mode - blocks all commands unconditionally
487
+ * - CPU_THRESHOLD: Enqueue mode - blocks all commands unconditionally
488
+ * - DISK_THRESHOLD: One-at-a-time mode - allows exactly one command when nothing is processing
489
+ *
490
+ * See: https://github.com/link-assistant/hive-mind/issues/1155
485
491
  *
486
- * @returns {Promise<{ok: boolean, reasons: string[]}>}
492
+ * @param {number} totalProcessing - Total processing count (queue + external claude processes)
493
+ * @returns {Promise<{ok: boolean, reasons: string[], oneAtATime: boolean}>}
487
494
  */
488
- async checkSystemResources() {
495
+ async checkSystemResources(totalProcessing = 0) {
489
496
  const reasons = [];
497
+ let oneAtATime = false;
490
498
 
491
499
  // Check RAM (using cached value)
500
+ // Enqueue mode: blocks all commands unconditionally
492
501
  const memResult = await getCachedMemoryInfo(this.verbose);
493
502
  if (memResult.success) {
494
503
  const usedRatio = memResult.memory.usedPercentage / 100;
@@ -499,6 +508,7 @@ export class SolveQueue {
499
508
  }
500
509
 
501
510
  // Check CPU using 5-minute load average (more stable than 1-minute)
511
+ // Enqueue mode: blocks all commands unconditionally
502
512
  // Cache TTL is 2 minutes, which is appropriate for this metric
503
513
  const cpuResult = await getCachedCpuInfo(this.verbose);
504
514
  if (cpuResult.success) {
@@ -522,22 +532,26 @@ export class SolveQueue {
522
532
  }
523
533
 
524
534
  // Check disk space (using cached value)
525
- // Note: Disk threshold is an ultimate restriction - it blocks unconditionally when triggered
526
- // Unlike CLAUDE_5_HOUR_SESSION_THRESHOLD and CLAUDE_WEEKLY_THRESHOLD which use totalProcessing
527
- // to allow one command at a time, disk threshold blocks all new commands immediately
528
- // See: https://github.com/link-assistant/hive-mind/issues/1133
535
+ // One-at-a-time mode: allows exactly one command when nothing is processing
536
+ // Unlike RAM and CPU which block unconditionally, disk uses one-at-a-time mode
537
+ // because we cannot predict how much disk space a task will use
538
+ // See: https://github.com/link-assistant/hive-mind/issues/1155
529
539
  const diskResult = await getCachedDiskInfo(this.verbose);
530
540
  if (diskResult.success) {
531
541
  // Calculate usage from free percentage
532
542
  const usedPercent = 100 - diskResult.diskSpace.freePercentage;
533
543
  const usedRatio = usedPercent / 100;
534
544
  if (usedRatio >= QUEUE_CONFIG.DISK_THRESHOLD) {
535
- reasons.push(formatWaitingReason('disk', usedPercent, QUEUE_CONFIG.DISK_THRESHOLD));
545
+ oneAtATime = true;
536
546
  this.recordThrottle('disk_high');
547
+ // Only block if something is already processing (one-at-a-time mode)
548
+ if (totalProcessing > 0) {
549
+ reasons.push(formatWaitingReason('disk', usedPercent, QUEUE_CONFIG.DISK_THRESHOLD) + ' (waiting for current command)');
550
+ }
537
551
  }
538
552
  }
539
553
 
540
- return { ok: reasons.length === 0, reasons };
554
+ return { ok: reasons.length === 0, reasons, oneAtATime };
541
555
  }
542
556
 
543
557
  /**