@link-assistant/hive-mind 1.11.4 â 1.11.6
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 +22 -0
- package/package.json +1 -1
- package/src/config.lib.mjs +1 -1
- package/src/solve.mjs +8 -2
- package/src/solve.results.lib.mjs +8 -4
- package/src/telegram-bot.mjs +1 -1
- package/src/telegram-solve-queue.lib.mjs +367 -137
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.11.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5eef9e4: Skip Claude API limits for --tool agent tasks in queue
|
|
8
|
+
- Agent tools (Grok Code, OpenCode Zen) use different backends with their own rate limits
|
|
9
|
+
- Add tool parameter to canStartCommand() and checkApiLimits() functions
|
|
10
|
+
- Skip Claude-specific limits (5-hour session, weekly) when tool is 'agent'
|
|
11
|
+
- Consumer loop now passes next queue item's tool to limit checks
|
|
12
|
+
- Add 7 new tests for tool-specific limit handling
|
|
13
|
+
- Add case study documentation
|
|
14
|
+
|
|
15
|
+
Fixes #1159
|
|
16
|
+
|
|
17
|
+
## 1.11.5
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 7d3387c: Fix duplicate Solution Draft Log comments on GitHub PRs
|
|
22
|
+
|
|
23
|
+
When a Claude session ends with uncommitted changes and --attach-logs is enabled, the solution draft log was being uploaded twice - once by verifyResults() during normal completion, and again after temporary watch mode completes. This fix tracks whether logs were already uploaded and skips the duplicate upload.
|
|
24
|
+
|
|
3
25
|
## 1.11.4
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/package.json
CHANGED
package/src/config.lib.mjs
CHANGED
|
@@ -194,7 +194,7 @@ export const cacheTtl = {
|
|
|
194
194
|
api: parseIntWithDefault('HIVE_MIND_API_CACHE_TTL_MS', 3 * 60 * 1000), // 3 minutes
|
|
195
195
|
// Claude Usage API cache TTL - must be at least 20 minutes to avoid rate limiting
|
|
196
196
|
// The API returns null values when called too frequently
|
|
197
|
-
usageApi: parseIntWithDefault('HIVE_MIND_USAGE_API_CACHE_TTL_MS',
|
|
197
|
+
usageApi: parseIntWithDefault('HIVE_MIND_USAGE_API_CACHE_TTL_MS', 10 * 60 * 1000), // 10 minutes
|
|
198
198
|
// System metrics cache TTL (RAM, CPU, disk)
|
|
199
199
|
system: parseIntWithDefault('HIVE_MIND_SYSTEM_CACHE_TTL_MS', 2 * 60 * 1000), // 2 minutes
|
|
200
200
|
};
|
package/src/solve.mjs
CHANGED
|
@@ -1159,7 +1159,9 @@ try {
|
|
|
1159
1159
|
// Pass shouldRestart to prevent early exit when auto-restart is needed
|
|
1160
1160
|
// Include agent tool pricing data when available (publicPricingEstimate, pricingInfo)
|
|
1161
1161
|
// Issue #1088: Pass errorDuringExecution for "Finished with errors" state
|
|
1162
|
-
|
|
1162
|
+
// Issue #1154: Track if logs were already uploaded to prevent duplicates
|
|
1163
|
+
const verifyResult = await verifyResults(owner, repo, branchName, issueNumber, prNumber, prUrl, referenceTime, argv, shouldAttachLogs, shouldRestart, sessionId, tempDir, anthropicTotalCostUSD, publicPricingEstimate, pricingInfo, errorDuringExecution);
|
|
1164
|
+
const logsAlreadyUploaded = verifyResult?.logUploadSuccess || false;
|
|
1163
1165
|
|
|
1164
1166
|
// Start watch mode if enabled OR if we need to handle uncommitted changes
|
|
1165
1167
|
if (argv.verbose) {
|
|
@@ -1246,7 +1248,8 @@ try {
|
|
|
1246
1248
|
}
|
|
1247
1249
|
|
|
1248
1250
|
// Attach updated logs to PR after auto-restart completes
|
|
1249
|
-
if
|
|
1251
|
+
// Issue #1154: Skip if logs were already uploaded by verifyResults() to prevent duplicates
|
|
1252
|
+
if (shouldAttachLogs && prNumber && !logsAlreadyUploaded) {
|
|
1250
1253
|
await log('đ Uploading working session logs to Pull Request...');
|
|
1251
1254
|
try {
|
|
1252
1255
|
const logUploadSuccess = await attachLogToGitHub({
|
|
@@ -1273,6 +1276,9 @@ try {
|
|
|
1273
1276
|
} catch (uploadError) {
|
|
1274
1277
|
await log(`â ī¸ Error uploading logs: ${uploadError.message}`, { level: 'warning' });
|
|
1275
1278
|
}
|
|
1279
|
+
} else if (logsAlreadyUploaded) {
|
|
1280
|
+
await log('âšī¸ Logs already uploaded by verifyResults, skipping duplicate upload', { verbose: true });
|
|
1281
|
+
logsAttached = true;
|
|
1276
1282
|
}
|
|
1277
1283
|
}
|
|
1278
1284
|
|
|
@@ -564,7 +564,8 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
|
|
|
564
564
|
if (!argv.watch && !shouldRestart) {
|
|
565
565
|
await safeExit(0, 'Process completed successfully');
|
|
566
566
|
}
|
|
567
|
-
|
|
567
|
+
// Issue #1154: Return logUploadSuccess to prevent duplicate log uploads
|
|
568
|
+
return { logUploadSuccess }; // Return for watch mode or auto-restart
|
|
568
569
|
} else {
|
|
569
570
|
await log(` âšī¸ Found pull request #${pr.number} but it appears to be from a different session`);
|
|
570
571
|
}
|
|
@@ -627,7 +628,8 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
|
|
|
627
628
|
if (!argv.watch && !shouldRestart) {
|
|
628
629
|
await safeExit(0, 'Process completed successfully');
|
|
629
630
|
}
|
|
630
|
-
|
|
631
|
+
// Issue #1154: Return logUploadSuccess to prevent duplicate log uploads
|
|
632
|
+
return { logUploadSuccess: true }; // Return for watch mode or auto-restart
|
|
631
633
|
} else if (allComments.length > 0) {
|
|
632
634
|
await log(` âšī¸ Issue has ${allComments.length} existing comment(s)`);
|
|
633
635
|
} else {
|
|
@@ -645,7 +647,8 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
|
|
|
645
647
|
if (!argv.watch) {
|
|
646
648
|
await safeExit(0, 'Process completed successfully');
|
|
647
649
|
}
|
|
648
|
-
|
|
650
|
+
// Issue #1154: Return logUploadSuccess to prevent duplicate log uploads
|
|
651
|
+
return { logUploadSuccess: false }; // Return for watch mode
|
|
649
652
|
} catch (searchError) {
|
|
650
653
|
reportError(searchError, {
|
|
651
654
|
context: 'verify_pr_creation',
|
|
@@ -661,7 +664,8 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
|
|
|
661
664
|
if (!argv.watch) {
|
|
662
665
|
await safeExit(0, 'Process completed successfully');
|
|
663
666
|
}
|
|
664
|
-
|
|
667
|
+
// Issue #1154: Return logUploadSuccess to prevent duplicate log uploads
|
|
668
|
+
return { logUploadSuccess: false }; // Return for watch mode
|
|
665
669
|
}
|
|
666
670
|
};
|
|
667
671
|
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -1102,7 +1102,7 @@ bot.command(/^solve$/i, async ctx => {
|
|
|
1102
1102
|
return;
|
|
1103
1103
|
}
|
|
1104
1104
|
|
|
1105
|
-
const check = await solveQueue.canStartCommand();
|
|
1105
|
+
const check = await solveQueue.canStartCommand({ tool: solveTool }); // Skip Claude limits for agent (#1159)
|
|
1106
1106
|
const queueStats = solveQueue.getStats();
|
|
1107
1107
|
if (check.canStart && queueStats.queued === 0) {
|
|
1108
1108
|
const startingMessage = await ctx.reply(`đ Starting solve command...\n\n${infoBlock}`, { parse_mode: 'Markdown', reply_to_message_id: ctx.message.message_id });
|
|
@@ -41,9 +41,9 @@ export const QUEUE_CONFIG = {
|
|
|
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.
|
|
45
|
-
CLAUDE_WEEKLY_THRESHOLD: 0.
|
|
46
|
-
GITHUB_API_THRESHOLD: 0.
|
|
44
|
+
CLAUDE_5_HOUR_SESSION_THRESHOLD: 0.75, // One-at-a-time if 5-hour limit >= 75%
|
|
45
|
+
CLAUDE_WEEKLY_THRESHOLD: 0.97, // One-at-a-time if weekly limit >= 97%
|
|
46
|
+
GITHUB_API_THRESHOLD: 0.75, // Enqueue if GitHub >= 75% with parallel claude
|
|
47
47
|
|
|
48
48
|
// Timing
|
|
49
49
|
// MIN_START_INTERVAL_MS: Time to allow solve command to start actual claude process
|
|
@@ -242,6 +242,13 @@ class SolveQueueItem {
|
|
|
242
242
|
|
|
243
243
|
/**
|
|
244
244
|
* Solve Queue - Producer/Consumer queue for /solve commands
|
|
245
|
+
*
|
|
246
|
+
* Uses separate queues for each tool type to ensure:
|
|
247
|
+
* - Claude tasks never block agent tasks (and vice versa)
|
|
248
|
+
* - Each tool queue maintains FIFO order
|
|
249
|
+
* - Each tool has independent rate limiting
|
|
250
|
+
*
|
|
251
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
245
252
|
*/
|
|
246
253
|
export class SolveQueue {
|
|
247
254
|
constructor(options = {}) {
|
|
@@ -249,14 +256,23 @@ export class SolveQueue {
|
|
|
249
256
|
this.executeCallback = options.executeCallback || null;
|
|
250
257
|
this.messageUpdateCallback = options.messageUpdateCallback || null;
|
|
251
258
|
|
|
252
|
-
//
|
|
253
|
-
|
|
259
|
+
// Separate queues per tool type - claude tasks never block agent tasks
|
|
260
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
261
|
+
this.queues = {
|
|
262
|
+
claude: [],
|
|
263
|
+
agent: [],
|
|
264
|
+
};
|
|
254
265
|
this.processing = new Map();
|
|
255
266
|
this.completed = [];
|
|
256
267
|
this.failed = [];
|
|
257
268
|
this.isRunning = true;
|
|
258
269
|
|
|
259
|
-
// Timing
|
|
270
|
+
// Timing - separate per tool to ensure independent processing
|
|
271
|
+
this.lastStartTimeByTool = {
|
|
272
|
+
claude: null,
|
|
273
|
+
agent: null,
|
|
274
|
+
};
|
|
275
|
+
// Legacy: keep for compatibility with existing code that uses lastStartTime
|
|
260
276
|
this.lastStartTime = null;
|
|
261
277
|
|
|
262
278
|
// Consumer task reference
|
|
@@ -272,7 +288,43 @@ export class SolveQueue {
|
|
|
272
288
|
throttleReasons: {},
|
|
273
289
|
};
|
|
274
290
|
|
|
275
|
-
this.log('SolveQueue initialized');
|
|
291
|
+
this.log('SolveQueue initialized with separate tool queues');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Get the queue array for a specific tool, creating it if needed
|
|
296
|
+
* @param {string} tool - Tool type ('claude', 'agent', etc.)
|
|
297
|
+
* @returns {Array} The queue array for this tool
|
|
298
|
+
*/
|
|
299
|
+
getToolQueue(tool) {
|
|
300
|
+
if (!this.queues[tool]) {
|
|
301
|
+
this.queues[tool] = [];
|
|
302
|
+
}
|
|
303
|
+
return this.queues[tool];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Get combined queue length across all tools (for backwards compatibility)
|
|
308
|
+
* @returns {number} Total queue length
|
|
309
|
+
*/
|
|
310
|
+
get queue() {
|
|
311
|
+
let total = [];
|
|
312
|
+
for (const toolQueue of Object.values(this.queues)) {
|
|
313
|
+
total = total.concat(toolQueue);
|
|
314
|
+
}
|
|
315
|
+
return total;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get total pending count across all tool queues
|
|
320
|
+
* @returns {number} Total pending items
|
|
321
|
+
*/
|
|
322
|
+
getTotalQueueLength() {
|
|
323
|
+
let total = 0;
|
|
324
|
+
for (const toolQueue of Object.values(this.queues)) {
|
|
325
|
+
total += toolQueue.length;
|
|
326
|
+
}
|
|
327
|
+
return total;
|
|
276
328
|
}
|
|
277
329
|
|
|
278
330
|
/**
|
|
@@ -286,16 +338,19 @@ export class SolveQueue {
|
|
|
286
338
|
}
|
|
287
339
|
|
|
288
340
|
/**
|
|
289
|
-
* Add a solve command to the queue
|
|
341
|
+
* Add a solve command to the appropriate tool queue
|
|
342
|
+
* Items are added to the queue for their specific tool type.
|
|
290
343
|
* @param {Object} options - Queue item options
|
|
291
344
|
* @returns {SolveQueueItem} The queued item
|
|
345
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
292
346
|
*/
|
|
293
347
|
enqueue(options) {
|
|
294
348
|
const item = new SolveQueueItem(options);
|
|
295
|
-
this.
|
|
349
|
+
const toolQueue = this.getToolQueue(item.tool);
|
|
350
|
+
toolQueue.push(item);
|
|
296
351
|
this.stats.totalEnqueued++;
|
|
297
352
|
|
|
298
|
-
this.log(`Enqueued: ${item.toString()}, queue length: ${
|
|
353
|
+
this.log(`Enqueued: ${item.toString()} to ${item.tool} queue, queue length: ${toolQueue.length}`);
|
|
299
354
|
|
|
300
355
|
// Start consumer if not already running
|
|
301
356
|
this.ensureConsumerRunning();
|
|
@@ -304,17 +359,19 @@ export class SolveQueue {
|
|
|
304
359
|
}
|
|
305
360
|
|
|
306
361
|
/**
|
|
307
|
-
* Find an item by URL in
|
|
362
|
+
* Find an item by URL in any queue or processing items
|
|
308
363
|
* Used to prevent duplicate URLs from being added to the queue
|
|
309
364
|
* @param {string} url - The URL to search for
|
|
310
365
|
* @returns {SolveQueueItem|null} The found item or null
|
|
311
366
|
* @see https://github.com/link-assistant/hive-mind/issues/1080
|
|
312
367
|
*/
|
|
313
368
|
findByUrl(url) {
|
|
314
|
-
// Check
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
369
|
+
// Check all tool queues
|
|
370
|
+
for (const toolQueue of Object.values(this.queues)) {
|
|
371
|
+
const queuedItem = toolQueue.find(item => item.url === url);
|
|
372
|
+
if (queuedItem) {
|
|
373
|
+
return queuedItem;
|
|
374
|
+
}
|
|
318
375
|
}
|
|
319
376
|
|
|
320
377
|
// Check processing items
|
|
@@ -329,17 +386,22 @@ export class SolveQueue {
|
|
|
329
386
|
|
|
330
387
|
/**
|
|
331
388
|
* Cancel a queued item by ID
|
|
389
|
+
* Searches all tool queues to find the item.
|
|
332
390
|
* @param {string} id - Item ID
|
|
333
391
|
* @returns {boolean} True if cancelled
|
|
392
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
334
393
|
*/
|
|
335
394
|
cancel(id) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
395
|
+
// Search all tool queues
|
|
396
|
+
for (const [tool, toolQueue] of Object.entries(this.queues)) {
|
|
397
|
+
const queueIndex = toolQueue.findIndex(item => item.id === id);
|
|
398
|
+
if (queueIndex !== -1) {
|
|
399
|
+
const item = toolQueue.splice(queueIndex, 1)[0];
|
|
400
|
+
item.setCancelled();
|
|
401
|
+
this.stats.totalCancelled++;
|
|
402
|
+
this.log(`Cancelled queued item: ${item.toString()} from ${tool} queue`);
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
343
405
|
}
|
|
344
406
|
|
|
345
407
|
if (this.processing.has(id)) {
|
|
@@ -355,39 +417,130 @@ export class SolveQueue {
|
|
|
355
417
|
* @returns {Object}
|
|
356
418
|
*/
|
|
357
419
|
getStats() {
|
|
420
|
+
// Calculate per-tool queue stats
|
|
421
|
+
const queuedByTool = {};
|
|
422
|
+
let totalQueued = 0;
|
|
423
|
+
for (const [tool, toolQueue] of Object.entries(this.queues)) {
|
|
424
|
+
queuedByTool[tool] = toolQueue.length;
|
|
425
|
+
totalQueued += toolQueue.length;
|
|
426
|
+
}
|
|
427
|
+
|
|
358
428
|
return {
|
|
359
|
-
queued:
|
|
429
|
+
queued: totalQueued,
|
|
430
|
+
queuedByTool,
|
|
360
431
|
processing: this.processing.size,
|
|
361
432
|
completed: this.completed.length,
|
|
362
433
|
failed: this.failed.length,
|
|
363
434
|
...this.stats,
|
|
364
435
|
cacheStats: getLimitCache().getStats(),
|
|
365
436
|
lastStartTime: this.lastStartTime,
|
|
437
|
+
lastStartTimeByTool: this.lastStartTimeByTool,
|
|
366
438
|
isRunning: this.isRunning,
|
|
367
439
|
};
|
|
368
440
|
}
|
|
369
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Count processing items by tool type
|
|
444
|
+
* Used for tool-specific limit checking - e.g., Claude limits only count Claude processing items
|
|
445
|
+
* @param {string} tool - Tool type to count ('claude', 'agent', etc.)
|
|
446
|
+
* @returns {number} Count of processing items with the specified tool
|
|
447
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
448
|
+
*/
|
|
449
|
+
getProcessingCountByTool(tool) {
|
|
450
|
+
let count = 0;
|
|
451
|
+
for (const item of this.processing.values()) {
|
|
452
|
+
if (item.tool === tool) {
|
|
453
|
+
count++;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return count;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Find startable items from each tool queue
|
|
461
|
+
* Returns the first item from each tool queue that can start.
|
|
462
|
+
* With separate queues, each tool is checked independently so they don't block each other.
|
|
463
|
+
* @returns {Promise<Array<{item: SolveQueueItem, tool: string, index: number, check: Object}>>}
|
|
464
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
465
|
+
*/
|
|
466
|
+
async findStartableItems() {
|
|
467
|
+
const startableItems = [];
|
|
468
|
+
|
|
469
|
+
for (const [tool, toolQueue] of Object.entries(this.queues)) {
|
|
470
|
+
if (toolQueue.length === 0) continue;
|
|
471
|
+
|
|
472
|
+
// Check if first item in this tool's queue can start
|
|
473
|
+
const item = toolQueue[0];
|
|
474
|
+
const check = await this.canStartCommand({ tool });
|
|
475
|
+
|
|
476
|
+
if (check.canStart) {
|
|
477
|
+
// Also check one-at-a-time mode for this specific tool
|
|
478
|
+
// For tool-specific one-at-a-time, only count that tool's processing items
|
|
479
|
+
const toolProcessingCount = this.getProcessingCountByTool(tool);
|
|
480
|
+
if (check.oneAtATime && toolProcessingCount > 0) {
|
|
481
|
+
// This tool is in one-at-a-time mode and has items processing
|
|
482
|
+
// Skip but don't block other tools
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
startableItems.push({ item, tool, index: 0, check });
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return startableItems;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Find first queue item that can start based on its tool's limits (legacy compatibility)
|
|
494
|
+
* With separate queues, returns the first startable item from any tool queue.
|
|
495
|
+
* @returns {Promise<{item: SolveQueueItem|null, index: number, check: Object}>}
|
|
496
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
497
|
+
*/
|
|
498
|
+
async findStartableItem() {
|
|
499
|
+
const startableItems = await this.findStartableItems();
|
|
500
|
+
if (startableItems.length > 0) {
|
|
501
|
+
// Return the first startable item (arbitrary order among tools)
|
|
502
|
+
const first = startableItems[0];
|
|
503
|
+
return { item: first.item, index: first.index, check: first.check };
|
|
504
|
+
}
|
|
505
|
+
return { item: null, index: -1, check: null };
|
|
506
|
+
}
|
|
507
|
+
|
|
370
508
|
/**
|
|
371
509
|
* Get queue items summary for display
|
|
510
|
+
* Combines items from all tool queues into a single pending list.
|
|
372
511
|
* @returns {Object}
|
|
512
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
373
513
|
*/
|
|
374
514
|
getQueueSummary() {
|
|
515
|
+
// Collect pending items from all tool queues
|
|
516
|
+
const pending = [];
|
|
517
|
+
for (const [tool, toolQueue] of Object.entries(this.queues)) {
|
|
518
|
+
for (const item of toolQueue) {
|
|
519
|
+
pending.push({
|
|
520
|
+
id: item.id,
|
|
521
|
+
url: item.url,
|
|
522
|
+
requester: item.requester,
|
|
523
|
+
waitTime: item.getWaitTime(),
|
|
524
|
+
createdAt: item.createdAt,
|
|
525
|
+
status: item.status,
|
|
526
|
+
waitingReason: item.waitingReason,
|
|
527
|
+
tool,
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Sort by createdAt to show oldest first (global order)
|
|
533
|
+
pending.sort((a, b) => a.createdAt - b.createdAt);
|
|
534
|
+
|
|
375
535
|
return {
|
|
376
|
-
pending
|
|
377
|
-
id: item.id,
|
|
378
|
-
url: item.url,
|
|
379
|
-
requester: item.requester,
|
|
380
|
-
waitTime: item.getWaitTime(),
|
|
381
|
-
createdAt: item.createdAt,
|
|
382
|
-
status: item.status,
|
|
383
|
-
waitingReason: item.waitingReason,
|
|
384
|
-
})),
|
|
536
|
+
pending,
|
|
385
537
|
processing: Array.from(this.processing.values()).map(item => ({
|
|
386
538
|
id: item.id,
|
|
387
539
|
url: item.url,
|
|
388
540
|
requester: item.requester,
|
|
389
541
|
startedAt: item.startedAt,
|
|
390
542
|
status: item.status,
|
|
543
|
+
tool: item.tool,
|
|
391
544
|
})),
|
|
392
545
|
};
|
|
393
546
|
}
|
|
@@ -400,15 +553,26 @@ export class SolveQueue {
|
|
|
400
553
|
* 2. Commands can run in parallel as long as actual limits are not exceeded
|
|
401
554
|
* 3. When any limit >= threshold, allow exactly one claude command to pass
|
|
402
555
|
*
|
|
556
|
+
* Logic per issue #1159:
|
|
557
|
+
* - Different tools have different limits. Claude limits only apply to 'claude' tool.
|
|
558
|
+
* - Processing count for Claude limits only includes Claude items, not agent items.
|
|
559
|
+
* - This allows agent tasks to run in parallel when Claude limits are reached.
|
|
560
|
+
*
|
|
561
|
+
* @param {Object} options - Options for the check
|
|
562
|
+
* @param {string} options.tool - The tool being used ('claude', 'agent', etc.)
|
|
403
563
|
* @returns {Promise<{canStart: boolean, reason?: string, reasons?: string[], oneAtATime?: boolean}>}
|
|
404
564
|
*/
|
|
405
|
-
async canStartCommand() {
|
|
565
|
+
async canStartCommand(options = {}) {
|
|
566
|
+
const tool = options.tool || 'claude';
|
|
406
567
|
const reasons = [];
|
|
407
568
|
let oneAtATime = false;
|
|
408
569
|
|
|
409
|
-
// Check minimum interval since last start
|
|
410
|
-
|
|
411
|
-
|
|
570
|
+
// Check minimum interval since last start FOR THIS TOOL
|
|
571
|
+
// Each tool queue has independent timing to prevent cross-blocking
|
|
572
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
573
|
+
const lastStartTime = this.lastStartTimeByTool[tool] || null;
|
|
574
|
+
if (lastStartTime) {
|
|
575
|
+
const timeSinceLastStart = Date.now() - lastStartTime;
|
|
412
576
|
if (timeSinceLastStart < QUEUE_CONFIG.MIN_START_INTERVAL_MS) {
|
|
413
577
|
const waitSeconds = Math.ceil((QUEUE_CONFIG.MIN_START_INTERVAL_MS - timeSinceLastStart) / 1000);
|
|
414
578
|
reasons.push(formatWaitingReason('min_interval', 0, 0) + ` (${waitSeconds}s remaining)`);
|
|
@@ -420,18 +584,23 @@ export class SolveQueue {
|
|
|
420
584
|
const claudeProcs = await getRunningClaudeProcesses(this.verbose);
|
|
421
585
|
const hasRunningClaude = claudeProcs.count > 0;
|
|
422
586
|
|
|
423
|
-
// Calculate total processing count
|
|
424
|
-
//
|
|
425
|
-
// to allow exactly one command at a time when threshold is reached
|
|
426
|
-
// See: https://github.com/link-assistant/hive-mind/issues/1133
|
|
587
|
+
// Calculate total processing count for system resources (all tools)
|
|
588
|
+
// System resources (RAM, CPU, disk) apply to all tools
|
|
427
589
|
const totalProcessing = this.processing.size + claudeProcs.count;
|
|
428
590
|
|
|
591
|
+
// Calculate Claude-specific processing count for Claude API limits
|
|
592
|
+
// Only counts Claude items in queue + external claude processes
|
|
593
|
+
// Agent items don't count against Claude's one-at-a-time limit
|
|
594
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
595
|
+
const claudeProcessingCount = this.getProcessingCountByTool('claude');
|
|
596
|
+
|
|
429
597
|
// Track claude_running as a metric (but don't add to reasons yet)
|
|
430
598
|
if (hasRunningClaude) {
|
|
431
599
|
this.recordThrottle('claude_running');
|
|
432
600
|
}
|
|
433
601
|
|
|
434
602
|
// Check system resources (RAM, CPU block unconditionally; disk uses one-at-a-time mode)
|
|
603
|
+
// System resources apply to ALL tools, not just Claude
|
|
435
604
|
// See: https://github.com/link-assistant/hive-mind/issues/1155
|
|
436
605
|
const resourceCheck = await this.checkSystemResources(totalProcessing);
|
|
437
606
|
if (!resourceCheck.ok) {
|
|
@@ -441,8 +610,11 @@ export class SolveQueue {
|
|
|
441
610
|
oneAtATime = true;
|
|
442
611
|
}
|
|
443
612
|
|
|
444
|
-
// Check API limits (pass hasRunningClaude and
|
|
445
|
-
|
|
613
|
+
// Check API limits (pass hasRunningClaude, claudeProcessingCount, and tool)
|
|
614
|
+
// Claude limits use claudeProcessingCount (only Claude items), not totalProcessing
|
|
615
|
+
// This allows agent tasks to proceed when Claude limits are reached
|
|
616
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
617
|
+
const limitCheck = await this.checkApiLimits(hasRunningClaude, claudeProcessingCount, tool);
|
|
446
618
|
if (!limitCheck.ok) {
|
|
447
619
|
reasons.push(...limitCheck.reasons);
|
|
448
620
|
}
|
|
@@ -472,6 +644,7 @@ export class SolveQueue {
|
|
|
472
644
|
oneAtATime,
|
|
473
645
|
claudeProcesses: claudeProcs.count,
|
|
474
646
|
totalProcessing,
|
|
647
|
+
claudeProcessingCount,
|
|
475
648
|
};
|
|
476
649
|
}
|
|
477
650
|
|
|
@@ -559,54 +732,77 @@ export class SolveQueue {
|
|
|
559
732
|
*
|
|
560
733
|
* Logic per issue #1133:
|
|
561
734
|
* - CLAUDE_5_HOUR_SESSION_THRESHOLD and CLAUDE_WEEKLY_THRESHOLD use one-at-a-time mode:
|
|
562
|
-
* when above threshold, allow exactly one command, block if
|
|
735
|
+
* when above threshold, allow exactly one command, block if claudeProcessing > 0
|
|
563
736
|
* - GitHub threshold blocks unconditionally when exceeded (ultimate restriction)
|
|
564
|
-
* - totalProcessing = queue-internal count + external claude processes (pgrep)
|
|
565
737
|
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
738
|
+
* Logic per issue #1159:
|
|
739
|
+
* - When tool is 'agent', skip Claude-specific limits entirely since agent uses different
|
|
740
|
+
* rate limits (Grok Code or similar). Only system resources and GitHub limits apply.
|
|
741
|
+
* - For Claude limits, only count Claude-specific processing items, not agent items.
|
|
742
|
+
* This allows agent tasks to run in parallel even when Claude limits are reached.
|
|
743
|
+
*
|
|
744
|
+
* @param {boolean} hasRunningClaude - Whether claude processes are running (from pgrep)
|
|
745
|
+
* @param {number} claudeProcessingCount - Count of 'claude' tool items being processed in queue
|
|
746
|
+
* @param {string} tool - The tool being used ('claude', 'agent', etc.)
|
|
568
747
|
* @returns {Promise<{ok: boolean, reasons: string[], oneAtATime: boolean}>}
|
|
569
748
|
*/
|
|
570
|
-
async checkApiLimits(hasRunningClaude = false,
|
|
749
|
+
async checkApiLimits(hasRunningClaude = false, claudeProcessingCount = 0, tool = 'claude') {
|
|
571
750
|
const reasons = [];
|
|
572
751
|
let oneAtATime = false;
|
|
573
752
|
|
|
753
|
+
// Apply Claude-specific limits only when tool is 'claude'
|
|
754
|
+
// Other tools (like 'agent') use different rate limiting backends and are not
|
|
755
|
+
// affected by Claude API limits (5-hour session, weekly limits)
|
|
756
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
757
|
+
const applyClaudeLimits = tool === 'claude';
|
|
758
|
+
|
|
759
|
+
// Calculate total Claude processing: queue-internal claude items + external claude processes
|
|
760
|
+
// This is used for Claude limits one-at-a-time mode - only counts Claude-related processing
|
|
761
|
+
// Agent items in the queue don't count against Claude's one-at-a-time limit
|
|
762
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
763
|
+
const totalClaudeProcessing = claudeProcessingCount + (hasRunningClaude ? 1 : 0);
|
|
764
|
+
|
|
574
765
|
// Check Claude limits (using cached value)
|
|
575
|
-
|
|
576
|
-
if (
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
766
|
+
// Only applied when tool is 'claude'
|
|
767
|
+
if (applyClaudeLimits) {
|
|
768
|
+
const claudeResult = await getCachedClaudeLimits(this.verbose);
|
|
769
|
+
if (claudeResult.success) {
|
|
770
|
+
const sessionPercent = claudeResult.usage.currentSession.percentage;
|
|
771
|
+
const weeklyPercent = claudeResult.usage.allModels.percentage;
|
|
772
|
+
|
|
773
|
+
// Session limit (5-hour)
|
|
774
|
+
// When above threshold: allow exactly one Claude command, block if any Claude processing
|
|
775
|
+
// Only counts Claude-specific processing, not agent items
|
|
776
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1133, #1159
|
|
777
|
+
if (sessionPercent !== null) {
|
|
778
|
+
const sessionRatio = sessionPercent / 100;
|
|
779
|
+
if (sessionRatio >= QUEUE_CONFIG.CLAUDE_5_HOUR_SESSION_THRESHOLD) {
|
|
780
|
+
oneAtATime = true;
|
|
781
|
+
this.recordThrottle(sessionRatio >= 1.0 ? 'claude_5_hour_session_100' : 'claude_5_hour_session_high');
|
|
782
|
+
// Use totalClaudeProcessing for Claude-specific one-at-a-time checking
|
|
783
|
+
if (totalClaudeProcessing > 0) {
|
|
784
|
+
reasons.push(formatWaitingReason('claude_5_hour_session', sessionPercent, QUEUE_CONFIG.CLAUDE_5_HOUR_SESSION_THRESHOLD) + ' (waiting for current command)');
|
|
785
|
+
}
|
|
592
786
|
}
|
|
593
787
|
}
|
|
594
|
-
}
|
|
595
788
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
789
|
+
// Weekly limit
|
|
790
|
+
// When above threshold: allow exactly one Claude command, block if one is in progress
|
|
791
|
+
if (weeklyPercent !== null) {
|
|
792
|
+
const weeklyRatio = weeklyPercent / 100;
|
|
793
|
+
if (weeklyRatio >= QUEUE_CONFIG.CLAUDE_WEEKLY_THRESHOLD) {
|
|
794
|
+
oneAtATime = true;
|
|
795
|
+
this.recordThrottle(weeklyRatio >= 1.0 ? 'claude_weekly_100' : 'claude_weekly_high');
|
|
796
|
+
// Use totalClaudeProcessing for Claude-specific one-at-a-time checking
|
|
797
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1133, #1159
|
|
798
|
+
if (totalClaudeProcessing > 0) {
|
|
799
|
+
reasons.push(formatWaitingReason('claude_weekly', weeklyPercent, QUEUE_CONFIG.CLAUDE_WEEKLY_THRESHOLD) + ' (waiting for current command)');
|
|
800
|
+
}
|
|
607
801
|
}
|
|
608
802
|
}
|
|
609
803
|
}
|
|
804
|
+
} else if (this.verbose) {
|
|
805
|
+
this.log(`Claude limits not applied for --tool ${tool}`);
|
|
610
806
|
}
|
|
611
807
|
|
|
612
808
|
// Check GitHub limits (only relevant if claude processes running)
|
|
@@ -678,88 +874,101 @@ export class SolveQueue {
|
|
|
678
874
|
}
|
|
679
875
|
|
|
680
876
|
/**
|
|
681
|
-
* Consumer loop - processes items from
|
|
877
|
+
* Consumer loop - processes items from all tool queues
|
|
878
|
+
*
|
|
879
|
+
* With separate queues per tool:
|
|
880
|
+
* - Each tool queue is checked independently
|
|
881
|
+
* - Claude limits only affect Claude queue
|
|
882
|
+
* - Agent queue can proceed even when Claude is blocked (and vice versa)
|
|
883
|
+
* - Multiple items can start in the same cycle (one per tool)
|
|
884
|
+
*
|
|
885
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
682
886
|
*/
|
|
683
887
|
async runConsumer() {
|
|
684
|
-
this.log('Consumer started');
|
|
888
|
+
this.log('Consumer started with separate tool queues');
|
|
685
889
|
|
|
686
890
|
while (this.isRunning) {
|
|
687
|
-
if
|
|
891
|
+
// Check if all queues are empty
|
|
892
|
+
if (this.getTotalQueueLength() === 0) {
|
|
688
893
|
await this.sleep(QUEUE_CONFIG.CONSUMER_POLL_INTERVAL_MS);
|
|
689
894
|
continue;
|
|
690
895
|
}
|
|
691
896
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
// Also periodically refresh messages to show current status
|
|
697
|
-
// See: https://github.com/link-assistant/hive-mind/issues/1078
|
|
698
|
-
for (const item of this.queue) {
|
|
699
|
-
if (item.status === QueueItemStatus.QUEUED || item.status === QueueItemStatus.WAITING) {
|
|
700
|
-
const previousStatus = item.status;
|
|
701
|
-
const previousReason = item.waitingReason;
|
|
702
|
-
item.setWaiting(check.reason);
|
|
703
|
-
|
|
704
|
-
// Update message if:
|
|
705
|
-
// 1. Status or reason changed
|
|
706
|
-
// 2. OR it's time for a periodic update (every MESSAGE_UPDATE_INTERVAL_MS)
|
|
707
|
-
const shouldUpdate = previousStatus !== item.status || previousReason !== item.waitingReason || this.shouldUpdateMessage(item);
|
|
708
|
-
|
|
709
|
-
if (shouldUpdate) {
|
|
710
|
-
const position = this.queue.indexOf(item) + 1;
|
|
711
|
-
await this.updateItemMessage(item, `âŗ Waiting (position #${position})\n\n${item.infoBlock}\n\n*Reason:*\n${check.reason}`);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
897
|
+
// Find startable items from each tool queue
|
|
898
|
+
// Each tool is checked independently so they don't block each other
|
|
899
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
900
|
+
const startableItems = await this.findStartableItems();
|
|
715
901
|
|
|
716
|
-
|
|
902
|
+
if (startableItems.length === 0) {
|
|
903
|
+
// No items can start - update all queued items with their tool-specific waiting reasons
|
|
904
|
+
await this.updateAllWaitingItems();
|
|
905
|
+
this.log(`Throttled: no items can start from any tool queue`);
|
|
717
906
|
await this.sleep(QUEUE_CONFIG.CONSUMER_POLL_INTERVAL_MS);
|
|
718
907
|
continue;
|
|
719
908
|
}
|
|
720
909
|
|
|
721
|
-
//
|
|
722
|
-
//
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
if (check.oneAtATime && check.totalProcessing > 0) {
|
|
727
|
-
const processInfo = check.claudeProcesses > 0 ? ` (${check.claudeProcesses} claude process${check.claudeProcesses > 1 ? 'es' : ''} running)` : '';
|
|
728
|
-
this.log(`One-at-a-time mode: waiting for current command to finish${processInfo}`);
|
|
729
|
-
await this.sleep(QUEUE_CONFIG.CONSUMER_POLL_INTERVAL_MS);
|
|
730
|
-
continue;
|
|
731
|
-
}
|
|
910
|
+
// Start items from each tool that can proceed
|
|
911
|
+
// This allows parallel starts from different tool queues
|
|
912
|
+
for (const startable of startableItems) {
|
|
913
|
+
const { tool } = startable;
|
|
914
|
+
const toolQueue = this.getToolQueue(tool);
|
|
732
915
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
916
|
+
// Remove the first item from this tool's queue
|
|
917
|
+
const item = toolQueue.shift();
|
|
918
|
+
if (!item) continue;
|
|
736
919
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
// See: https://github.com/link-assistant/hive-mind/issues/1078
|
|
920
|
+
// Update status to Starting
|
|
921
|
+
item.setStarting();
|
|
922
|
+
this.processing.set(item.id, item);
|
|
741
923
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
this.stats.totalStarted++;
|
|
924
|
+
// Update tool-specific last start time
|
|
925
|
+
this.lastStartTimeByTool[tool] = Date.now();
|
|
926
|
+
this.lastStartTime = Date.now(); // Legacy compatibility
|
|
927
|
+
this.stats.totalStarted++;
|
|
747
928
|
|
|
748
|
-
|
|
749
|
-
|
|
929
|
+
// Update message to show Starting status
|
|
930
|
+
await this.updateItemMessage(item, `đ Starting solve command...\n\n${item.infoBlock}`);
|
|
750
931
|
|
|
751
|
-
|
|
932
|
+
this.log(`Starting: ${item.toString()} from ${tool} queue`);
|
|
752
933
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
934
|
+
// Execute in background
|
|
935
|
+
this.executeItem(item).catch(error => {
|
|
936
|
+
console.error(`[solve-queue] Execution error for ${item.id}:`, error);
|
|
937
|
+
});
|
|
938
|
+
}
|
|
757
939
|
}
|
|
758
940
|
|
|
759
941
|
this.log('Consumer stopped');
|
|
760
942
|
this.consumerTask = null;
|
|
761
943
|
}
|
|
762
944
|
|
|
945
|
+
/**
|
|
946
|
+
* Update all waiting items with their tool-specific waiting reasons
|
|
947
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1078
|
|
948
|
+
*/
|
|
949
|
+
async updateAllWaitingItems() {
|
|
950
|
+
for (const [tool, toolQueue] of Object.entries(this.queues)) {
|
|
951
|
+
for (let i = 0; i < toolQueue.length; i++) {
|
|
952
|
+
const item = toolQueue[i];
|
|
953
|
+
if (item.status === QueueItemStatus.QUEUED || item.status === QueueItemStatus.WAITING) {
|
|
954
|
+
// Get the specific reason for this item's tool
|
|
955
|
+
const itemCheck = await this.canStartCommand({ tool: item.tool });
|
|
956
|
+
const previousStatus = item.status;
|
|
957
|
+
const previousReason = item.waitingReason;
|
|
958
|
+
item.setWaiting(itemCheck.reason || 'Waiting in queue');
|
|
959
|
+
|
|
960
|
+
// Update message if status/reason changed or it's time for periodic update
|
|
961
|
+
const shouldUpdate = previousStatus !== item.status || previousReason !== item.waitingReason || this.shouldUpdateMessage(item);
|
|
962
|
+
|
|
963
|
+
if (shouldUpdate) {
|
|
964
|
+
const position = i + 1; // Position within this tool's queue
|
|
965
|
+
await this.updateItemMessage(item, `âŗ Waiting (${tool} queue #${position})\n\n${item.infoBlock}\n\n*Reason:*\n${item.waitingReason}`);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
763
972
|
/**
|
|
764
973
|
* Execute a queue item
|
|
765
974
|
* @param {SolveQueueItem} item
|
|
@@ -870,26 +1079,47 @@ export class SolveQueue {
|
|
|
870
1079
|
|
|
871
1080
|
/**
|
|
872
1081
|
* Format queue status for display
|
|
1082
|
+
* Shows per-tool queue counts.
|
|
873
1083
|
* @returns {string}
|
|
1084
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
874
1085
|
*/
|
|
875
1086
|
formatStatus() {
|
|
876
1087
|
const stats = this.getStats();
|
|
877
1088
|
if (stats.queued > 0 || stats.processing > 0) {
|
|
878
|
-
|
|
1089
|
+
// Show per-tool breakdown if there are items
|
|
1090
|
+
const toolBreakdown = Object.entries(stats.queuedByTool)
|
|
1091
|
+
.filter(entry => entry[1] > 0)
|
|
1092
|
+
.map(([tool, count]) => `${tool}: ${count}`)
|
|
1093
|
+
.join(', ');
|
|
1094
|
+
const queueInfo = toolBreakdown ? ` (${toolBreakdown})` : '';
|
|
1095
|
+
return `Solve Queue: ${stats.queued} pending${queueInfo}, ${stats.processing} processing\n`;
|
|
879
1096
|
}
|
|
880
1097
|
return 'Solve Queue: empty\n';
|
|
881
1098
|
}
|
|
882
1099
|
|
|
883
1100
|
/**
|
|
884
1101
|
* Format detailed queue status for Telegram message
|
|
1102
|
+
* Shows per-tool queue breakdown.
|
|
885
1103
|
* @returns {string}
|
|
1104
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
886
1105
|
*/
|
|
887
1106
|
formatDetailedStatus() {
|
|
888
1107
|
const stats = this.getStats();
|
|
889
1108
|
const summary = this.getQueueSummary();
|
|
890
1109
|
|
|
891
1110
|
let message = 'đ *Solve Queue Status*\n\n';
|
|
892
|
-
message += `Pending: ${stats.queued}
|
|
1111
|
+
message += `Pending: ${stats.queued}`;
|
|
1112
|
+
|
|
1113
|
+
// Add per-tool breakdown
|
|
1114
|
+
const toolBreakdown = Object.entries(stats.queuedByTool)
|
|
1115
|
+
.filter(entry => entry[1] > 0)
|
|
1116
|
+
.map(([tool, count]) => `${tool}: ${count}`)
|
|
1117
|
+
.join(', ');
|
|
1118
|
+
if (toolBreakdown) {
|
|
1119
|
+
message += ` (${toolBreakdown})`;
|
|
1120
|
+
}
|
|
1121
|
+
message += '\n';
|
|
1122
|
+
|
|
893
1123
|
message += `Processing: ${stats.processing}\n`;
|
|
894
1124
|
message += `Completed: ${stats.completed}\n`;
|
|
895
1125
|
message += `Failed: ${stats.failed}\n\n`;
|
|
@@ -897,7 +1127,7 @@ export class SolveQueue {
|
|
|
897
1127
|
if (summary.processing.length > 0) {
|
|
898
1128
|
message += '*Currently Processing:*\n';
|
|
899
1129
|
for (const item of summary.processing) {
|
|
900
|
-
message += `âĸ ${item.url}\n`;
|
|
1130
|
+
message += `âĸ ${item.url} [${item.tool}]\n`;
|
|
901
1131
|
}
|
|
902
1132
|
message += '\n';
|
|
903
1133
|
}
|
|
@@ -906,7 +1136,7 @@ export class SolveQueue {
|
|
|
906
1136
|
message += '*Waiting in Queue:*\n';
|
|
907
1137
|
for (const item of summary.pending.slice(0, 5)) {
|
|
908
1138
|
const waitSeconds = Math.floor(item.waitTime / 1000);
|
|
909
|
-
message += `âĸ ${item.url} (${item.status}, ${waitSeconds}s)\n`;
|
|
1139
|
+
message += `âĸ ${item.url} [${item.tool}] (${item.status}, ${waitSeconds}s)\n`;
|
|
910
1140
|
if (item.waitingReason) {
|
|
911
1141
|
message += ` â ${item.waitingReason}\n`;
|
|
912
1142
|
}
|