@link-assistant/hive-mind 1.49.0 → 1.49.2
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 +16 -0
- package/package.json +1 -1
- package/src/agent.prompts.lib.mjs +5 -2
- package/src/claude.prompts.lib.mjs +5 -2
- package/src/codex.prompts.lib.mjs +20 -2
- package/src/opencode.prompts.lib.mjs +20 -2
- package/src/session-monitor.lib.mjs +33 -0
- package/src/solve.auto-merge.lib.mjs +41 -11
- package/src/telegram-bot.mjs +13 -29
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.49.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 026c95c: fix: non-consistent auto-restart logic on comments (#1567)
|
|
8
|
+
- Reduce CI check interval from 5 minutes to 2 minutes for faster response times
|
|
9
|
+
- Prevent concurrent sessions on the same PR/issue via active session URL checking
|
|
10
|
+
- Add cross-process deduplication for "Ready to merge" comments
|
|
11
|
+
- Add initial 2-minute cooldown before first mergeable check to ensure proper ordering
|
|
12
|
+
|
|
13
|
+
## 1.49.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 00512d6: Fix broken screenshot URL in fork mode: use forked repo path instead of original repo path in screenshot URL template when operating in fork mode (#1561).
|
|
18
|
+
|
|
3
19
|
## 1.49.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -82,7 +82,10 @@ export const buildUserPrompt = params => {
|
|
|
82
82
|
* @returns {string} The formatted system prompt
|
|
83
83
|
*/
|
|
84
84
|
export const buildSystemPrompt = params => {
|
|
85
|
-
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv, modelSupportsVision } = params;
|
|
85
|
+
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv, modelSupportsVision, forkedRepo } = params;
|
|
86
|
+
|
|
87
|
+
// When in fork mode, screenshots are pushed to the fork, not the original repo
|
|
88
|
+
const screenshotRepoPath = argv?.fork && forkedRepo ? forkedRepo : `${owner}/${repo}`;
|
|
86
89
|
|
|
87
90
|
// Build thinking instruction based on --think level
|
|
88
91
|
let thinkLine = '';
|
|
@@ -245,7 +248,7 @@ GitHub CLI command patterns.
|
|
|
245
248
|
Visual UI work and screenshots.
|
|
246
249
|
- When you work on visual UI changes (frontend, CSS, HTML, design), include a render or screenshot of the final result in the pull request description.
|
|
247
250
|
- When you need to show visual results, take a screenshot and save it to the repository (e.g., in a docs/screenshots/ or assets/ folder).
|
|
248
|
-
- When you save screenshots to the repository, use permanent links in the pull request description markdown (e.g., https://github.com/${
|
|
251
|
+
- When you save screenshots to the repository, use permanent links in the pull request description markdown (e.g., https://github.com/${screenshotRepoPath}/blob/${branchName}/docs/screenshots/result.png?raw=true).
|
|
249
252
|
- When uploading images, commit them to the branch first, then reference them using the GitHub blob URL format with ?raw=true suffix (works for both public and private repositories).
|
|
250
253
|
- When the visual result is important for review, mention it explicitly in the pull request description with the embedded image.
|
|
251
254
|
- When fixing UI bugs, capture both the "before" (problem) and "after" (fixed) screenshots as evidence for human verification.
|
|
@@ -92,7 +92,10 @@ export const buildUserPrompt = params => {
|
|
|
92
92
|
* @returns {string} The formatted system prompt
|
|
93
93
|
*/
|
|
94
94
|
export const buildSystemPrompt = params => {
|
|
95
|
-
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv, modelSupportsVision } = params;
|
|
95
|
+
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv, modelSupportsVision, forkedRepo } = params;
|
|
96
|
+
|
|
97
|
+
// When in fork mode, screenshots are pushed to the fork, not the original repo
|
|
98
|
+
const screenshotRepoPath = argv?.fork && forkedRepo ? forkedRepo : `${owner}/${repo}`;
|
|
96
99
|
|
|
97
100
|
// Note: --think keywords are deprecated for Claude Code >= 2.1.12
|
|
98
101
|
// Thinking is now enabled by default with 31,999 token budget
|
|
@@ -339,7 +342,7 @@ Agent Commander usage (unified subagent delegation).
|
|
|
339
342
|
Visual UI work and screenshots.
|
|
340
343
|
- When you work on visual UI changes (frontend, CSS, HTML, design), include a render or screenshot of the final result in the pull request description.
|
|
341
344
|
- When you need to show visual results, take a screenshot and save it to the repository (e.g., in a docs/screenshots/ or assets/ folder).
|
|
342
|
-
- When you save screenshots to the repository, use permanent links in the pull request description markdown (e.g., https://github.com/${
|
|
345
|
+
- When you save screenshots to the repository, use permanent links in the pull request description markdown (e.g., https://github.com/${screenshotRepoPath}/blob/${branchName}/docs/screenshots/result.png?raw=true).
|
|
343
346
|
- When uploading images, commit them to the branch first, then reference them using the GitHub blob URL format with ?raw=true suffix (works for both public and private repositories).
|
|
344
347
|
- When the visual result is important for review, mention it explicitly in the pull request description with the embedded image.
|
|
345
348
|
- When fixing UI bugs, capture both the "before" (problem) and "after" (fixed) screenshots as evidence for human verification.
|
|
@@ -82,7 +82,10 @@ export const buildUserPrompt = params => {
|
|
|
82
82
|
* @returns {string} The formatted system prompt
|
|
83
83
|
*/
|
|
84
84
|
export const buildSystemPrompt = params => {
|
|
85
|
-
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv } = params;
|
|
85
|
+
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv, modelSupportsVision, forkedRepo } = params;
|
|
86
|
+
|
|
87
|
+
// When in fork mode, screenshots are pushed to the fork, not the original repo
|
|
88
|
+
const screenshotRepoPath = argv?.fork && forkedRepo ? forkedRepo : `${owner}/${repo}`;
|
|
86
89
|
|
|
87
90
|
// Build thinking instruction based on --think level
|
|
88
91
|
let thinkLine = '';
|
|
@@ -246,7 +249,22 @@ GitHub CLI command patterns.
|
|
|
246
249
|
- When adding PR comment, use gh pr comment NUMBER --body "text" --repo OWNER/REPO.
|
|
247
250
|
- When adding issue comment, use gh issue comment NUMBER --body "text" --repo OWNER/REPO.
|
|
248
251
|
- When viewing PR details, use gh pr view NUMBER --repo OWNER/REPO.
|
|
249
|
-
- When filtering with jq, use gh api repos/\${owner}/\${repo}/pulls/\${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${
|
|
252
|
+
- When filtering with jq, use gh api repos/\${owner}/\${repo}/pulls/\${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${
|
|
253
|
+
modelSupportsVision
|
|
254
|
+
? `
|
|
255
|
+
|
|
256
|
+
Visual UI work and screenshots.
|
|
257
|
+
- When you work on visual UI changes (frontend, CSS, HTML, design), include a render or screenshot of the final result in the pull request description.
|
|
258
|
+
- When you need to show visual results, take a screenshot and save it to the repository (e.g., in a docs/screenshots/ or assets/ folder).
|
|
259
|
+
- When you save screenshots to the repository, use permanent links in the pull request description markdown (e.g., https://github.com/${screenshotRepoPath}/blob/${branchName}/docs/screenshots/result.png?raw=true).
|
|
260
|
+
- When uploading images, commit them to the branch first, then reference them using the GitHub blob URL format with ?raw=true suffix (works for both public and private repositories).
|
|
261
|
+
- When the visual result is important for review, mention it explicitly in the pull request description with the embedded image.
|
|
262
|
+
- When fixing UI bugs, capture both the "before" (problem) and "after" (fixed) screenshots as evidence for human verification.
|
|
263
|
+
- When reporting UI bugs, include a screenshot of the problem state to enable visual verification of the fix.
|
|
264
|
+
- When the fix is visual, include side-by-side or sequential comparison of before/after states in the PR description.
|
|
265
|
+
- When possible, create automated visual regression tests to prevent the UI bug from recurring.`
|
|
266
|
+
: ''
|
|
267
|
+
}${ciExamples}${getArchitectureCareSubPrompt(argv)}`;
|
|
250
268
|
};
|
|
251
269
|
|
|
252
270
|
// Export all functions as default object too
|
|
@@ -82,7 +82,10 @@ export const buildUserPrompt = params => {
|
|
|
82
82
|
* @returns {string} The formatted system prompt
|
|
83
83
|
*/
|
|
84
84
|
export const buildSystemPrompt = params => {
|
|
85
|
-
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv } = params;
|
|
85
|
+
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv, modelSupportsVision, forkedRepo } = params;
|
|
86
|
+
|
|
87
|
+
// When in fork mode, screenshots are pushed to the fork, not the original repo
|
|
88
|
+
const screenshotRepoPath = argv?.fork && forkedRepo ? forkedRepo : `${owner}/${repo}`;
|
|
86
89
|
|
|
87
90
|
// Build thinking instruction based on --think level
|
|
88
91
|
let thinkLine = '';
|
|
@@ -239,7 +242,22 @@ GitHub CLI command patterns.
|
|
|
239
242
|
- When adding PR comment, use gh pr comment NUMBER --body "text" --repo OWNER/REPO.
|
|
240
243
|
- When adding issue comment, use gh issue comment NUMBER --body "text" --repo OWNER/REPO.
|
|
241
244
|
- When viewing PR details, use gh pr view NUMBER --repo OWNER/REPO.
|
|
242
|
-
- When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${
|
|
245
|
+
- When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${
|
|
246
|
+
modelSupportsVision
|
|
247
|
+
? `
|
|
248
|
+
|
|
249
|
+
Visual UI work and screenshots.
|
|
250
|
+
- When you work on visual UI changes (frontend, CSS, HTML, design), include a render or screenshot of the final result in the pull request description.
|
|
251
|
+
- When you need to show visual results, take a screenshot and save it to the repository (e.g., in a docs/screenshots/ or assets/ folder).
|
|
252
|
+
- When you save screenshots to the repository, use permanent links in the pull request description markdown (e.g., https://github.com/${screenshotRepoPath}/blob/${branchName}/docs/screenshots/result.png?raw=true).
|
|
253
|
+
- When uploading images, commit them to the branch first, then reference them using the GitHub blob URL format with ?raw=true suffix (works for both public and private repositories).
|
|
254
|
+
- When the visual result is important for review, mention it explicitly in the pull request description with the embedded image.
|
|
255
|
+
- When fixing UI bugs, capture both the "before" (problem) and "after" (fixed) screenshots as evidence for human verification.
|
|
256
|
+
- When reporting UI bugs, include a screenshot of the problem state to enable visual verification of the fix.
|
|
257
|
+
- When the fix is visual, include side-by-side or sequential comparison of before/after states in the PR description.
|
|
258
|
+
- When possible, create automated visual regression tests to prevent the UI bug from recurring.`
|
|
259
|
+
: ''
|
|
260
|
+
}${ciExamples}${getArchitectureCareSubPrompt(argv)}`;
|
|
243
261
|
};
|
|
244
262
|
|
|
245
263
|
// Export all functions as default object too
|
|
@@ -243,6 +243,39 @@ export function startSessionMonitoring(bot, verbose = false, intervalMs = 30000)
|
|
|
243
243
|
return timer;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Issue #1567: Check if there's an active session for a given URL.
|
|
248
|
+
* This prevents concurrent sessions on the same PR/issue, which causes
|
|
249
|
+
* iteration number jumps, duplicate "Ready to merge" comments, and other
|
|
250
|
+
* inconsistencies when two auto-restart-until-mergeable processes run
|
|
251
|
+
* simultaneously.
|
|
252
|
+
*
|
|
253
|
+
* @param {string} url - The GitHub URL to check (issue or PR URL)
|
|
254
|
+
* @param {boolean} verbose - Whether to log verbose output
|
|
255
|
+
* @returns {{isActive: boolean, sessionName: string|null}} Whether an active session exists for this URL
|
|
256
|
+
*/
|
|
257
|
+
export function hasActiveSessionForUrl(url, verbose = false) {
|
|
258
|
+
if (!url) return { isActive: false, sessionName: null };
|
|
259
|
+
|
|
260
|
+
// Normalize the URL for comparison (remove trailing slashes, fragments, etc.)
|
|
261
|
+
const normalizeUrl = u => u.replace(/\/+$/, '').replace(/#.*$/, '').toLowerCase();
|
|
262
|
+
const normalizedUrl = normalizeUrl(url);
|
|
263
|
+
|
|
264
|
+
for (const [sessionName, sessionInfo] of activeSessions.entries()) {
|
|
265
|
+
if (sessionInfo.url && normalizeUrl(sessionInfo.url) === normalizedUrl) {
|
|
266
|
+
if (verbose) {
|
|
267
|
+
console.log(`[VERBOSE] Found active session for URL ${url}: ${sessionName}`);
|
|
268
|
+
}
|
|
269
|
+
return { isActive: true, sessionName };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (verbose) {
|
|
274
|
+
console.log(`[VERBOSE] No active session found for URL ${url}`);
|
|
275
|
+
}
|
|
276
|
+
return { isActive: false, sessionName: null };
|
|
277
|
+
}
|
|
278
|
+
|
|
246
279
|
/**
|
|
247
280
|
* Get statistics about session tracking
|
|
248
281
|
* @param {boolean} verbose - Whether to log verbose output
|
|
@@ -510,9 +510,11 @@ export const watchUntilMergeable = async params => {
|
|
|
510
510
|
const { issueUrl, owner, repo, issueNumber, prNumber, prBranch, branchName, tempDir, argv } = params;
|
|
511
511
|
|
|
512
512
|
const rawWatchInterval = argv.watchInterval || 60; // seconds
|
|
513
|
-
// Issue #1503: Enforce minimum
|
|
514
|
-
//
|
|
515
|
-
|
|
513
|
+
// Issue #1503: Enforce minimum CI check interval to conserve GitHub API rate limits.
|
|
514
|
+
// Issue #1567: Reduced from 5 minutes (300s) to 2 minutes (120s) to decrease wait times
|
|
515
|
+
// between working session finish and "Ready to merge" / next action detection.
|
|
516
|
+
// This also applies uniformly whether CI/CD is configured or not.
|
|
517
|
+
const MIN_CI_CHECK_INTERVAL_SECONDS = 120;
|
|
516
518
|
const watchInterval = Math.max(rawWatchInterval, MIN_CI_CHECK_INTERVAL_SECONDS);
|
|
517
519
|
const isAutoMerge = argv.autoMerge || false;
|
|
518
520
|
// Issue #1503: --wait-for-all-actions-in-repository-before-mergable (default: true)
|
|
@@ -549,11 +551,20 @@ export const watchUntilMergeable = async params => {
|
|
|
549
551
|
let consecutiveNoRunsChecks = 0;
|
|
550
552
|
let lastKnownHeadSha = null;
|
|
551
553
|
|
|
554
|
+
// Issue #1567: Initial cooldown before first check.
|
|
555
|
+
// Wait at least MIN_CI_CHECK_INTERVAL_SECONDS after working session finishes before
|
|
556
|
+
// starting to check. This ensures:
|
|
557
|
+
// 1. Solution Draft Log is fully posted before any "Ready to merge" can appear
|
|
558
|
+
// 2. CI/CD checks have time to register with GitHub (avoids false "no CI" detection)
|
|
559
|
+
// 3. Consistent behavior whether CI/CD is configured or not
|
|
560
|
+
const INITIAL_COOLDOWN_SECONDS = MIN_CI_CHECK_INTERVAL_SECONDS;
|
|
561
|
+
|
|
552
562
|
await log('');
|
|
553
563
|
await log(formatAligned('🔄', 'AUTO-RESTART-UNTIL-MERGEABLE MODE ACTIVE', ''));
|
|
554
564
|
await log(formatAligned('', 'Monitoring PR:', `#${prNumber}`, 2));
|
|
555
565
|
await log(formatAligned('', 'Mode:', isAutoMerge ? 'Auto-merge (will merge when ready)' : 'Auto-restart-until-mergeable (will NOT auto-merge)', 2));
|
|
556
566
|
await log(formatAligned('', 'Checking interval:', `${watchInterval} seconds (minimum: ${MIN_CI_CHECK_INTERVAL_SECONDS}s)`, 2));
|
|
567
|
+
await log(formatAligned('', 'Initial cooldown:', `${INITIAL_COOLDOWN_SECONDS} seconds`, 2));
|
|
557
568
|
await log(formatAligned('', 'Wait for all repo actions:', waitForAllRepoActionsFlag ? 'Yes (absolute safety)' : 'No', 2));
|
|
558
569
|
await log(formatAligned('', 'Stop conditions:', 'PR merged, PR closed, or becomes mergeable', 2));
|
|
559
570
|
await log(formatAligned('', 'Restart triggers:', 'New non-bot comments, CI failures, merge conflicts', 2));
|
|
@@ -561,6 +572,13 @@ export const watchUntilMergeable = async params => {
|
|
|
561
572
|
await log('Press Ctrl+C to stop watching manually');
|
|
562
573
|
await log('');
|
|
563
574
|
|
|
575
|
+
// Issue #1567: Wait for initial cooldown before first check.
|
|
576
|
+
// This gives CI/CD time to start and solution logs time to be posted.
|
|
577
|
+
await log(formatAligned('⏳', 'Initial cooldown:', `Waiting ${INITIAL_COOLDOWN_SECONDS}s before first check...`));
|
|
578
|
+
await new Promise(resolve => setTimeout(resolve, INITIAL_COOLDOWN_SECONDS * 1000));
|
|
579
|
+
await log(formatAligned('✅', 'Cooldown complete:', 'Starting monitoring loop'));
|
|
580
|
+
await log('');
|
|
581
|
+
|
|
564
582
|
let iteration = 0;
|
|
565
583
|
let lastCheckTime = new Date();
|
|
566
584
|
|
|
@@ -732,16 +750,28 @@ export const watchUntilMergeable = async params => {
|
|
|
732
750
|
await log(formatAligned('', 'Exiting auto-restart-until-mergeable mode', '', 2));
|
|
733
751
|
|
|
734
752
|
// Issue #1371: Post success comment only if not already posted in this session.
|
|
735
|
-
//
|
|
736
|
-
//
|
|
737
|
-
//
|
|
753
|
+
// Issue #1567: Also check PR comment history as a cross-process guard.
|
|
754
|
+
// Two layers of deduplication:
|
|
755
|
+
// 1. In-memory flag (readyToMergeCommentPosted) — prevents duplicates within this process
|
|
756
|
+
// 2. checkForExistingComment — prevents duplicates from concurrent processes
|
|
757
|
+
// The in-memory flag is reset when HEAD SHA changes (line 614), so a new commit
|
|
758
|
+
// will allow a fresh "Ready to merge" comment.
|
|
738
759
|
try {
|
|
739
760
|
if (!readyToMergeCommentPosted) {
|
|
740
|
-
// Issue #
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
761
|
+
// Issue #1567: Cross-process deduplication — check if another process already
|
|
762
|
+
// posted a "Ready to merge" comment. This catches the case where two concurrent
|
|
763
|
+
// watchUntilMergeable processes both detect mergeability simultaneously.
|
|
764
|
+
const hasExistingReadyComment = await checkForExistingComment(owner, repo, prNumber, '## ✅ Ready to merge', argv.verbose);
|
|
765
|
+
if (hasExistingReadyComment) {
|
|
766
|
+
await log(formatAligned('', 'Skipping duplicate "Ready to merge" comment (already posted by another process)', '', 2));
|
|
767
|
+
readyToMergeCommentPosted = true;
|
|
768
|
+
} else {
|
|
769
|
+
// Issue #1345: Differentiate message when no CI is configured
|
|
770
|
+
const ciLine = noCiConfigured ? '- No CI/CD checks are configured for this repository' : noCiTriggered ? (workflowRunConclusions ? `- CI workflows completed without executing (${workflowRunConclusions})` : '- CI workflows exist but were not triggered for this commit') : '- All CI checks have passed';
|
|
771
|
+
const commentBody = `## ✅ Ready to merge\n\nThis pull request is now ready to be merged:\n${ciLine}\n- No merge conflicts\n- No pending changes\n\n---\n*Monitored by hive-mind with --auto-restart-until-mergeable flag*`;
|
|
772
|
+
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
773
|
+
readyToMergeCommentPosted = true;
|
|
774
|
+
}
|
|
745
775
|
} else {
|
|
746
776
|
await log(formatAligned('', 'Skipping duplicate "Ready to merge" comment (already posted this session)', '', 2));
|
|
747
777
|
}
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -48,7 +48,7 @@ const { getSolveQueue, createQueueExecuteCallback } = await import('./telegram-s
|
|
|
48
48
|
const { isChatStopped, getChatStopInfo, getStoppedChatRejectMessage, DEFAULT_STOP_REASON } = await import('./telegram-start-stop-command.lib.mjs');
|
|
49
49
|
const { isOldMessage: _isOldMessage, isGroupChat: _isGroupChat, isChatAuthorized: _isChatAuthorized, isForwardedOrReply: _isForwardedOrReply, extractCommandFromText, extractGitHubUrl: _extractGitHubUrl } = await import('./telegram-message-filters.lib.mjs');
|
|
50
50
|
const { launchBotWithRetry } = await import('./telegram-bot-launcher.lib.mjs');
|
|
51
|
-
const { trackSession, startSessionMonitoring } = await import('./session-monitor.lib.mjs');
|
|
51
|
+
const { trackSession, startSessionMonitoring, hasActiveSessionForUrl } = await import('./session-monitor.lib.mjs');
|
|
52
52
|
|
|
53
53
|
const config = yargs(hideBin(process.argv))
|
|
54
54
|
.usage('Usage: hive-telegram-bot [options]')
|
|
@@ -238,11 +238,8 @@ if (hiveEnabled && hiveOverrides.length > 0) {
|
|
|
238
238
|
throw new Error(msg);
|
|
239
239
|
});
|
|
240
240
|
await testYargs.parse(testArgs);
|
|
241
|
-
// Issue #1482
|
|
242
|
-
|
|
243
|
-
if (overrideBranchError) {
|
|
244
|
-
throw new Error(overrideBranchError);
|
|
245
|
-
}
|
|
241
|
+
const overrideBranchError = validateBranchInArgs(hiveOverrides); // Issue #1482
|
|
242
|
+
if (overrideBranchError) throw new Error(overrideBranchError);
|
|
246
243
|
console.log('✅ Hive overrides validated successfully');
|
|
247
244
|
} finally {
|
|
248
245
|
// Restore stderr
|
|
@@ -755,17 +752,9 @@ bot.command('limits', async ctx => {
|
|
|
755
752
|
// Get all limits using shared cache (3min for API, 2min for system)
|
|
756
753
|
const limits = await getAllCachedLimits(VERBOSE);
|
|
757
754
|
|
|
758
|
-
// Format
|
|
759
|
-
// If Claude auth failed, pass the error to formatUsageMessage to show it in the Claude sections
|
|
760
|
-
// while still displaying all other limits sections (disk, GitHub, CPU, memory)
|
|
761
|
-
// See: https://github.com/link-assistant/hive-mind/issues/1343
|
|
755
|
+
// Format message with usage limits and queue status (issues #1343, #1267)
|
|
762
756
|
const claudeError = limits.claude.success ? null : limits.claude.error;
|
|
763
757
|
const solveQueue = getSolveQueue({ verbose: VERBOSE });
|
|
764
|
-
// Fetch queue status and pass it as an extra section to formatUsageMessage so that all
|
|
765
|
-
// sections are assembled before the code block is formed — no fragile string-searching needed.
|
|
766
|
-
// Shows each queue (claude, agent) with pending/processing counts.
|
|
767
|
-
// Processing counts are actual running system processes (via pgrep).
|
|
768
|
-
// See: https://github.com/link-assistant/hive-mind/issues/1267
|
|
769
758
|
const queueStatus = await solveQueue.formatStatus();
|
|
770
759
|
const message = '📊 *Usage Limits*\n\n' + formatUsageMessage(limits.claude.success ? limits.claude.usage : null, limits.disk.success ? limits.disk.diskSpace : null, limits.github.success ? limits.github.githubRateLimit : null, limits.cpu.success ? limits.cpu.cpuLoad : null, limits.memory.success ? limits.memory.memory : null, claudeError, [queueStatus]);
|
|
771
760
|
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, message, { parse_mode: 'Markdown' });
|
|
@@ -1007,22 +996,22 @@ async function handleSolveCommand(ctx) {
|
|
|
1007
996
|
if (solveOverrides.length > 0) infoBlock += `${userOptionsRaw ? '\n' : '\n\n'}🔒 Locked options: ${escapeMarkdown(solveOverrides.join(' '))}`;
|
|
1008
997
|
const solveQueue = getSolveQueue({ verbose: VERBOSE });
|
|
1009
998
|
|
|
1010
|
-
// Check for duplicate URL in queue
|
|
1011
|
-
// See: https://github.com/link-assistant/hive-mind/issues/1080
|
|
999
|
+
// Check for duplicate URL in queue (issue #1080)
|
|
1012
1000
|
const existingItem = solveQueue.findByUrl(normalizedUrl);
|
|
1013
1001
|
if (existingItem) {
|
|
1014
1002
|
const statusText = existingItem.status === 'starting' || existingItem.status === 'started' ? 'being processed' : 'already in the queue';
|
|
1015
1003
|
await safeReply(ctx, `❌ This URL is ${statusText}.\n\nURL: ${escapeMarkdown(normalizedUrl)}\nStatus: ${existingItem.status}\n\n💡 Use /solve_queue to check the queue status.`, { reply_to_message_id: ctx.message.message_id });
|
|
1016
1004
|
return;
|
|
1017
1005
|
}
|
|
1018
|
-
|
|
1006
|
+
// Issue #1567: Prevent concurrent sessions on the same PR/issue
|
|
1007
|
+
const activeSession = hasActiveSessionForUrl(normalizedUrl, VERBOSE);
|
|
1008
|
+
if (activeSession.isActive) {
|
|
1009
|
+
await safeReply(ctx, `❌ A working session is already running for this URL.\n\nURL: ${escapeMarkdown(normalizedUrl)}\nSession: \`${activeSession.sessionName}\`\n\n💡 Wait for the current session to complete, or use /solve\\_stop to cancel it.`, { reply_to_message_id: ctx.message.message_id });
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1019
1012
|
const check = await solveQueue.canStartCommand({ tool: solveTool }); // Skip Claude limits for agent (#1159)
|
|
1020
1013
|
const queueStats = solveQueue.getStats();
|
|
1021
|
-
|
|
1022
|
-
// Handle rejection: when a threshold strategy is 'reject', the command should fail immediately
|
|
1023
|
-
// without being placed in the queue. This ensures users get clear feedback about why
|
|
1024
|
-
// their command cannot be processed (e.g., disk full, server maintenance pending).
|
|
1025
|
-
// See: https://github.com/link-assistant/hive-mind/issues/1267
|
|
1014
|
+
// Handle rejection: threshold strategy is 'reject' — fail immediately (issue #1267)
|
|
1026
1015
|
if (check.rejected) {
|
|
1027
1016
|
await safeReply(ctx, `❌ Solve command rejected.\n\n${infoBlock}\n\n🚫 Reason: ${escapeMarkdown(check.rejectReason || 'Unknown')}`, { reply_to_message_id: ctx.message.message_id });
|
|
1028
1017
|
return;
|
|
@@ -1288,13 +1277,11 @@ bot.on('message', async (ctx, next) => {
|
|
|
1288
1277
|
bot.catch((error, ctx) => {
|
|
1289
1278
|
console.error('Unhandled error while processing update', ctx.update.update_id);
|
|
1290
1279
|
console.error('Error:', error);
|
|
1291
|
-
// Log detailed error information
|
|
1292
1280
|
console.error('Error details:', {
|
|
1293
1281
|
name: error.name,
|
|
1294
1282
|
message: error.message,
|
|
1295
1283
|
stack: error.stack?.split('\n').slice(0, 10).join('\n'),
|
|
1296
1284
|
});
|
|
1297
|
-
// Log context information for debugging
|
|
1298
1285
|
if (VERBOSE) {
|
|
1299
1286
|
console.log('[VERBOSE] Error context:', {
|
|
1300
1287
|
chatId: ctx.chat?.id,
|
|
@@ -1319,7 +1306,6 @@ bot.catch((error, ctx) => {
|
|
|
1319
1306
|
|
|
1320
1307
|
// Try to notify the user about the error with more details
|
|
1321
1308
|
if (ctx?.reply) {
|
|
1322
|
-
// Detect if this is a Telegram API parsing error
|
|
1323
1309
|
const isTelegramParsingError = error.message && (error.message.includes("can't parse entities") || error.message.includes("Can't parse entities") || error.message.includes("can't find end of") || (error.message.includes('Bad Request') && error.message.includes('400')));
|
|
1324
1310
|
|
|
1325
1311
|
let errorMessage;
|
|
@@ -1342,7 +1328,6 @@ bot.catch((error, ctx) => {
|
|
|
1342
1328
|
// Issue #1460: Show user a simple, non-confusing message — all details are in the logs
|
|
1343
1329
|
errorMessage = `❌ Failed to send formatted message. Please try your command again.\n\nIf the issue persists, contact support with Update ID: ${ctx.update.update_id}`;
|
|
1344
1330
|
} else {
|
|
1345
|
-
// Build informative error message for other errors
|
|
1346
1331
|
errorMessage = '❌ An error occurred while processing your request.\n\n';
|
|
1347
1332
|
if (error.message) {
|
|
1348
1333
|
// Filter out sensitive info and escape markdown
|
|
@@ -1358,8 +1343,7 @@ bot.catch((error, ctx) => {
|
|
|
1358
1343
|
if (VERBOSE) errorMessage += `\n\n🔍 Debug info: Update ID: ${ctx.update.update_id}`;
|
|
1359
1344
|
}
|
|
1360
1345
|
|
|
1361
|
-
// Issue #1460: For parsing errors
|
|
1362
|
-
// For other errors, try Markdown first, then fall back to plain text
|
|
1346
|
+
// Issue #1460: For parsing errors send plain text; otherwise try Markdown first
|
|
1363
1347
|
if (isTelegramParsingError) {
|
|
1364
1348
|
ctx.reply(errorMessage).catch(fallbackError => {
|
|
1365
1349
|
console.error('Failed to send plain text error message:', fallbackError);
|