@link-assistant/hive-mind 1.62.1 → 1.63.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.
@@ -342,7 +342,7 @@ function isForwardedOrReply(ctx) {
342
342
  /**
343
343
  * Validates the model name in the args array and returns an error message if invalid
344
344
  * @param {string[]} args - Array of command arguments
345
- * @param {string} tool - The tool to validate against ('claude' or 'opencode')
345
+ * @param {string} tool - The tool to validate against ('claude', 'opencode', 'codex', 'agent', or 'gemini')
346
346
  * @returns {string|null} Error message if invalid, null if valid or no model specified
347
347
  */
348
348
  function validateModelInArgs(args, tool = 'claude') {
@@ -525,7 +525,7 @@ bot.command('help', async ctx => {
525
525
  message += '📝 *Available Commands:*\n\n';
526
526
 
527
527
  if (solveEnabled) {
528
- message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */qwen*) - Solve a GitHub issue\n';
528
+ message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */gemini*, */qwen*) - Solve a GitHub issue\n';
529
529
  message += 'Usage: `/solve <github-url> [options]`\n';
530
530
  message += 'Example: `/solve https://github.com/owner/repo/issues/123 --model sonnet`\n';
531
531
  message += 'Tool aliases imply `--tool <tool>`: `/codex <github-url>` equals `/solve <github-url> --tool codex`\n';
@@ -535,7 +535,7 @@ bot.command('help', async ctx => {
535
535
  }
536
536
  message += '\n';
537
537
  } else {
538
- message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */qwen*) - ❌ Disabled\n\n';
538
+ message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */gemini*, */qwen*) - ❌ Disabled\n\n';
539
539
  }
540
540
 
541
541
  if (taskEnabled) {
@@ -576,7 +576,7 @@ bot.command('help', async ctx => {
576
576
  message += '🔔 *Session Notifications:* Completion notifications are automatic; use /subscribe for private DM forwards.\n';
577
577
  if (ISOLATION_BACKEND) message += `🔒 *Isolation Mode:* \`${ISOLATION_BACKEND}\` (experimental)\n`;
578
578
  message += '\n';
579
- message += '⚠️ *Note:* /solve, /do, /continue, /claude, /codex, /opencode, /agent, /qwen, /task, /split, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats. /terminal\\_watch, /subscribe and /unsubscribe work in private and group chats.\n\n';
579
+ message += '⚠️ *Note:* /solve, /do, /continue, /claude, /codex, /opencode, /agent, /gemini, /qwen, /task, /split, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats. /terminal\\_watch, /subscribe and /unsubscribe work in private and group chats.\n\n';
580
580
  message += '🔧 *Common Options:*\n';
581
581
  message += `• \`--model <model>\` or \`-m\` - ${buildModelOptionDescription()}\n`;
582
582
  message += '• `--base-branch <branch>` or `-b` - Target branch for PR (default: repo default branch)\n';
@@ -13,6 +13,7 @@ export const TOOL_SOLVE_COMMAND_ALIASES = Object.freeze({
13
13
  codex: 'codex',
14
14
  opencode: 'opencode',
15
15
  agent: 'agent',
16
+ gemini: 'gemini',
16
17
  qwen: 'qwen',
17
18
  });
18
19
 
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Features:
8
8
  * - Shows pending, processing, completed, and failed queue items
9
- * - Per-tool queue breakdown (claude, opencode, etc.)
9
+ * - Per-tool queue breakdown (claude, opencode, codex, agent, gemini, etc.)
10
10
  * - Lists currently processing and waiting items
11
11
  * - Running Claude process count
12
12
  *
@@ -7,7 +7,7 @@ const execAsync = promisify(exec);
7
7
 
8
8
  /**
9
9
  * Count running processes by name.
10
- * @param {string} processName - Process name to search for (e.g., 'claude', 'agent')
10
+ * @param {string} processName - Process name to search for (e.g., 'claude', 'agent', 'codex', 'gemini')
11
11
  * @param {boolean} verbose - Whether to log verbose output
12
12
  * @returns {Promise<{count: number, processes: string[]}>}
13
13
  */
@@ -75,6 +75,15 @@ export async function getRunningCodexProcesses(verbose = false) {
75
75
  return getRunningProcesses('codex', verbose);
76
76
  }
77
77
 
78
+ /**
79
+ * Count running gemini processes.
80
+ * @param {boolean} verbose - Whether to log verbose output
81
+ * @returns {Promise<{count: number, processes: string[]}>}
82
+ */
83
+ export async function getRunningGeminiProcesses(verbose = false) {
84
+ return getRunningProcesses('gemini', verbose);
85
+ }
86
+
78
87
  /**
79
88
  * Count running qwen processes.
80
89
  * @param {boolean} verbose - Whether to log verbose output
@@ -154,6 +163,8 @@ export function formatWaitingReason(metric, currentValue, threshold) {
154
163
  return 'Claude process is already running';
155
164
  case 'codex_running':
156
165
  return 'Codex process is already running';
166
+ case 'gemini_running':
167
+ return 'Gemini process is already running';
157
168
  case 'qwen_running':
158
169
  return 'Qwen Code process is already running';
159
170
  default:
@@ -16,8 +16,8 @@
16
16
  */
17
17
 
18
18
  import { getCachedClaudeLimits, getCachedCodexLimits, getCachedGitHubLimits, getCachedMemoryInfo, getCachedCpuInfo, getCachedDiskInfo, getLimitCache } from './limits.lib.mjs';
19
- export { formatDuration, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningCodexProcesses, getRunningProcesses, getRunningQwenProcesses } from './telegram-solve-queue.helpers.lib.mjs';
20
- import { formatDuration, formatWaitingReason, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningProcesses } from './telegram-solve-queue.helpers.lib.mjs';
19
+ export { formatDuration, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningCodexProcesses, getRunningGeminiProcesses, getRunningProcesses, getRunningQwenProcesses } from './telegram-solve-queue.helpers.lib.mjs';
20
+ import { formatDuration, formatWaitingReason, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningCodexProcesses, getRunningGeminiProcesses, getRunningProcesses, getRunningQwenProcesses } from './telegram-solve-queue.helpers.lib.mjs';
21
21
  export { QUEUE_CONFIG, THRESHOLD_STRATEGIES } from './queue-config.lib.mjs';
22
22
  import { QUEUE_CONFIG } from './queue-config.lib.mjs';
23
23
  import { formatExecutingWorkSessionMessage, formatStartingWorkSessionMessage } from './work-session-formatting.lib.mjs';
@@ -128,7 +128,7 @@ class SolveQueueItem {
128
128
  * Solve Queue - Producer/Consumer queue for /solve commands
129
129
  *
130
130
  * Uses separate queues for each tool type to ensure:
131
- * - Claude tasks never block agent tasks (and vice versa)
131
+ * - Claude tasks never block agent, codex, gemini, or qwen tasks (and vice versa)
132
132
  * - Each tool queue maintains FIFO order
133
133
  * - Each tool has independent rate limiting
134
134
  *
@@ -143,12 +143,13 @@ export class SolveQueue {
143
143
  this.getRunningIsolatedSessionsFn = options.getRunningIsolatedSessions || getRunningIsolatedSessions;
144
144
  this.autoStart = options.autoStart !== false;
145
145
 
146
- // Separate queues per tool type - claude tasks never block agent tasks
146
+ // Separate queues per tool type - claude tasks never block other tool tasks
147
147
  // See: https://github.com/link-assistant/hive-mind/issues/1159
148
148
  this.queues = {
149
149
  claude: [],
150
150
  agent: [],
151
151
  codex: [],
152
+ gemini: [],
152
153
  qwen: [],
153
154
  };
154
155
  this.processing = new Map();
@@ -161,6 +162,7 @@ export class SolveQueue {
161
162
  claude: null,
162
163
  agent: null,
163
164
  codex: null,
165
+ gemini: null,
164
166
  qwen: null,
165
167
  };
166
168
  // Legacy: keep for compatibility with existing code that uses lastStartTime
@@ -184,7 +186,7 @@ export class SolveQueue {
184
186
 
185
187
  /**
186
188
  * Get the queue array for a specific tool, creating it if needed
187
- * @param {string} tool - Tool type ('claude', 'agent', etc.)
189
+ * @param {string} tool - Tool type ('claude', 'agent', 'codex', 'gemini', etc.)
188
190
  * @returns {Array} The queue array for this tool
189
191
  */
190
192
  getToolQueue(tool) {
@@ -333,7 +335,7 @@ export class SolveQueue {
333
335
  /**
334
336
  * Count processing items by tool type
335
337
  * Used for tool-specific limit checking - e.g., Claude limits only count Claude processing items
336
- * @param {string} tool - Tool type to count ('claude', 'agent', etc.)
338
+ * @param {string} tool - Tool type to count ('claude', 'agent', 'codex', 'gemini', etc.)
337
339
  * @returns {number} Count of processing items with the specified tool
338
340
  * @see https://github.com/link-assistant/hive-mind/issues/1159
339
341
  */
@@ -378,6 +380,7 @@ export class SolveQueue {
378
380
 
379
381
  if (check.canStart) {
380
382
  const item = toolQueue[0];
383
+ if (!item) continue;
381
384
  // Also check one-at-a-time mode for this specific tool
382
385
  // For tool-specific one-at-a-time, only count that tool's processing items
383
386
  const toolProcessingCount = this.getProcessingCountByTool(tool);
@@ -397,7 +400,7 @@ export class SolveQueue {
397
400
  * Reject all items in a tool queue and notify users.
398
401
  * Called when a 'reject' strategy threshold is exceeded for queued items.
399
402
  *
400
- * @param {string} tool - Tool type (e.g., 'claude', 'agent')
403
+ * @param {string} tool - Tool type (e.g., 'claude', 'agent', 'codex', 'gemini')
401
404
  * @param {SolveQueueItem[]} toolQueue - The tool's queue array
402
405
  * @param {string} rejectReason - Reason for rejection
403
406
  * @see https://github.com/link-assistant/hive-mind/issues/1555
@@ -523,8 +526,8 @@ export class SolveQueue {
523
526
  *
524
527
  * Logic per issue #1159:
525
528
  * - Different tools have different limits. Claude limits only apply to 'claude' tool.
526
- * - Processing count for Claude limits only includes Claude items, not agent items.
527
- * - This allows agent tasks to run in parallel when Claude limits are reached.
529
+ * - Processing count for Claude limits only includes Claude items, not agent/codex/gemini/qwen items.
530
+ * - This allows non-Claude tasks to run in parallel when Claude limits are reached.
528
531
  *
529
532
  * Logic per issue #1253:
530
533
  * - All thresholds now support configurable strategies (reject, enqueue, dequeue-one-at-a-time)
@@ -533,7 +536,7 @@ export class SolveQueue {
533
536
  * - 'dequeue-one-at-a-time' allows one command while blocking subsequent
534
537
  *
535
538
  * @param {Object} options - Options for the check
536
- * @param {string} options.tool - The tool being used ('claude', 'agent', etc.)
539
+ * @param {string} options.tool - The tool being used ('claude', 'agent', 'codex', 'gemini', 'qwen', etc.)
537
540
  * @returns {Promise<{canStart: boolean, rejected?: boolean, rejectReason?: string, reason?: string, reasons?: string[], oneAtATime?: boolean}>}
538
541
  */
539
542
  async canStartCommand(options = {}) {
@@ -563,9 +566,11 @@ export class SolveQueue {
563
566
  const claudeProcessCount = externalProcessing.byTool.claude || 0;
564
567
  const codexProcessCount = externalProcessing.byTool.codex || 0;
565
568
  const agentProcessCount = externalProcessing.byTool.agent || 0;
569
+ const geminiProcessCount = externalProcessing.byTool.gemini || 0;
566
570
  const qwenProcessCount = externalProcessing.byTool.qwen || 0;
567
571
  const hasRunningClaude = claudeProcessCount > 0;
568
572
  const hasRunningCodex = codexProcessCount > 0;
573
+ const hasRunningGemini = geminiProcessCount > 0;
569
574
  const hasRunningQwen = qwenProcessCount > 0;
570
575
 
571
576
  // Calculate total processing count for system resources (all tools)
@@ -574,10 +579,11 @@ export class SolveQueue {
574
579
 
575
580
  // Calculate Claude-specific processing count for Claude API limits
576
581
  // Only counts Claude items in queue + external claude processes
577
- // Agent items don't count against Claude's one-at-a-time limit
582
+ // Non-Claude items don't count against Claude's one-at-a-time limit
578
583
  // See: https://github.com/link-assistant/hive-mind/issues/1159
579
584
  const claudeProcessingCount = this.getProcessingCountByTool('claude');
580
585
  const codexProcessingCount = this.getProcessingCountByTool('codex');
586
+ const geminiProcessingCount = this.getProcessingCountByTool('gemini');
581
587
  const qwenProcessingCount = this.getProcessingCountByTool('qwen');
582
588
 
583
589
  // Track claude_running as a metric (but don't add to reasons yet)
@@ -587,6 +593,9 @@ export class SolveQueue {
587
593
  if (hasRunningCodex) {
588
594
  this.recordThrottle('codex_running');
589
595
  }
596
+ if (hasRunningGemini) {
597
+ this.recordThrottle('gemini_running');
598
+ }
590
599
  if (hasRunningQwen) {
591
600
  this.recordThrottle('qwen_running');
592
601
  }
@@ -609,11 +618,11 @@ export class SolveQueue {
609
618
 
610
619
  // Check API limits with strategy support (pass hasRunningClaude, claudeProcessingCount, and tool)
611
620
  // Claude limits use claudeProcessingCount (only Claude items), not totalProcessing
612
- // This allows agent tasks to proceed when Claude limits are reached
621
+ // This allows non-Claude tasks to proceed when Claude limits are reached
613
622
  // See: https://github.com/link-assistant/hive-mind/issues/1159
614
623
  // See: https://github.com/link-assistant/hive-mind/issues/1253 (strategies)
615
- const hasRunningToolProcess = tool === 'codex' ? hasRunningCodex : tool === 'qwen' ? hasRunningQwen : hasRunningClaude;
616
- const toolProcessingCount = tool === 'codex' ? codexProcessingCount : tool === 'qwen' ? qwenProcessingCount : claudeProcessingCount;
624
+ const hasRunningToolProcess = (externalProcessing.byTool[tool] || 0) > 0;
625
+ const toolProcessingCount = this.getProcessingCountByTool(tool);
617
626
  const limitCheck = await this.checkApiLimits(hasRunningToolProcess, toolProcessingCount, tool);
618
627
  if (limitCheck.rejected) {
619
628
  rejected = true;
@@ -637,6 +646,9 @@ export class SolveQueue {
637
646
  if (tool === 'codex' && hasRunningCodex && reasons.length > 0) {
638
647
  reasons.push(formatWaitingReason('codex_running', codexProcessCount, 0) + ` (${codexProcessCount} processes)`);
639
648
  }
649
+ if (tool === 'gemini' && hasRunningGemini && reasons.length > 0) {
650
+ reasons.push(formatWaitingReason('gemini_running', geminiProcessCount, 0) + ` (${geminiProcessCount} processes)`);
651
+ }
640
652
  if (tool === 'qwen' && hasRunningQwen && reasons.length > 0) {
641
653
  reasons.push(formatWaitingReason('qwen_running', qwenProcessCount, 0) + ` (${qwenProcessCount} processes)`);
642
654
  }
@@ -661,11 +673,13 @@ export class SolveQueue {
661
673
  claudeProcesses: claudeProcessCount,
662
674
  codexProcesses: codexProcessCount,
663
675
  agentProcesses: agentProcessCount,
676
+ geminiProcesses: geminiProcessCount,
664
677
  qwenProcesses: qwenProcessCount,
665
678
  isolatedProcesses: externalProcessing.isolatedTotal,
666
679
  totalProcessing,
667
680
  claudeProcessingCount,
668
681
  codexProcessingCount,
682
+ geminiProcessingCount,
669
683
  qwenProcessingCount,
670
684
  };
671
685
  }
@@ -800,10 +814,10 @@ export class SolveQueue {
800
814
  * - GitHub threshold blocks unconditionally when exceeded (ultimate restriction)
801
815
  *
802
816
  * Logic per issue #1159:
803
- * - When tool is 'agent', skip Claude-specific limits entirely since agent uses different
804
- * rate limits (Grok Code or similar). Only system resources and GitHub limits apply.
805
- * - For Claude limits, only count Claude-specific processing items, not agent items.
806
- * This allows agent tasks to run in parallel even when Claude limits are reached.
817
+ * - When tool is 'agent', 'gemini', or 'qwen', skip Claude-specific limits entirely since these tools use
818
+ * different rate limiting backends. Only system resources and GitHub limits apply.
819
+ * - For Claude limits, only count Claude-specific processing items, not agent/codex/gemini/qwen items.
820
+ * This allows non-Claude tasks to run in parallel even when Claude limits are reached.
807
821
  *
808
822
  * Logic per issue #1253:
809
823
  * - All thresholds now support configurable strategies (reject, enqueue, dequeue-one-at-a-time)
@@ -811,7 +825,7 @@ export class SolveQueue {
811
825
  *
812
826
  * @param {boolean} hasRunningToolProcess - Whether matching tool processes are running (from pgrep)
813
827
  * @param {number} toolProcessingCount - Count of matching tool items being processed in queue
814
- * @param {string} tool - The tool being used ('claude', 'agent', etc.)
828
+ * @param {string} tool - The tool being used ('claude', 'agent', 'codex', 'gemini', 'qwen', etc.)
815
829
  * @returns {Promise<{ok: boolean, reasons: string[], oneAtATime: boolean, rejected: boolean, rejectReason: string|null}>}
816
830
  */
817
831
  async checkApiLimits(hasRunningToolProcess = false, toolProcessingCount = 0, tool = 'claude') {
@@ -821,7 +835,7 @@ export class SolveQueue {
821
835
  let rejectReason = null;
822
836
 
823
837
  // Apply Claude-specific limits only when tool is 'claude'
824
- // Other tools (like 'agent') use different rate limiting backends and are not
838
+ // Other tools (like 'agent', 'gemini', and 'qwen') use different rate limiting backends and are not
825
839
  // affected by Claude API limits (5-hour session, weekly limits)
826
840
  // See: https://github.com/link-assistant/hive-mind/issues/1159
827
841
  const applyClaudeLimits = tool === 'claude';
@@ -1427,6 +1441,9 @@ export default {
1427
1441
  getRunningProcesses,
1428
1442
  getRunningClaudeProcesses,
1429
1443
  getRunningAgentProcesses,
1444
+ getRunningCodexProcesses,
1445
+ getRunningGeminiProcesses,
1446
+ getRunningQwenProcesses,
1430
1447
  getRunningIsolatedSessions,
1431
1448
  createQueueExecuteCallback,
1432
1449
  formatDuration,
@@ -391,7 +391,7 @@ export function formatResetTimeWithRelative(resetTime, timezone = null) {
391
391
  * Format usage limit error message for console output
392
392
  *
393
393
  * @param {Object} options - Formatting options
394
- * @param {string} options.tool - Tool name (claude, codex, opencode)
394
+ * @param {string} options.tool - Tool name (claude, codex, opencode, agent, gemini)
395
395
  * @param {string|null} options.resetTime - Time when limit resets
396
396
  * @param {string|null} options.sessionId - Session ID for resuming
397
397
  * @param {string|null} options.resumeCommand - Command to resume session