@link-assistant/hive-mind 1.23.1 â 1.23.3
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 +21 -0
- package/package.json +1 -1
- package/src/agent.lib.mjs +4 -1
- package/src/solve.mjs +6 -3
- package/src/solve.watch.lib.mjs +65 -0
- package/src/telegram-merge-queue.lib.mjs +4 -2
- package/src/usage-limit.lib.mjs +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.23.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a797e56: fix: escape owner/repo names for Telegram MarkdownV2 in /merge command
|
|
8
|
+
|
|
9
|
+
Fixed the `/merge` command silently failing when updating Telegram messages for repositories with hyphens in their names (e.g., `link-assistant/hive-mind`). The issue was caused by unescaped special characters in MarkdownV2 format.
|
|
10
|
+
|
|
11
|
+
## 1.23.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 241ce36: Fix false error categorization and missing log upload for `--tool agent` auto-restart
|
|
16
|
+
- Fix `isUsageLimitError()` "resets" pattern causing false positives when scanning code output
|
|
17
|
+
- Changed from substring match to regex that requires time-like content after "resets"
|
|
18
|
+
- Prevents ordinary English words like "loads a shell and resets" from triggering usage limit detection
|
|
19
|
+
- Fix agent fallback pattern matching running after agent successfully recovered from errors
|
|
20
|
+
- Skip fallback when exitCode=0 and agentCompletedSuccessfully to prevent false error detection
|
|
21
|
+
- Upload failure logs when auto-restart iteration fails for `--tool agent` with `--attach-logs`
|
|
22
|
+
- Add comprehensive tests for false positive scenarios (Issue #1290)
|
|
23
|
+
|
|
3
24
|
## 1.23.1
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/package.json
CHANGED
package/src/agent.lib.mjs
CHANGED
|
@@ -784,7 +784,10 @@ export const executeAgentCommand = async params => {
|
|
|
784
784
|
// Issue #1258: Fallback pattern match for error detection
|
|
785
785
|
// When JSON parsing fails (e.g., multi-line pretty-printed JSON in logs),
|
|
786
786
|
// we need to detect error patterns in the raw output string
|
|
787
|
-
|
|
787
|
+
// Issue #1290: Skip fallback when agent completed successfully with exit code 0
|
|
788
|
+
// The fallback can cause false positives when error events (like AI_JSONParseError)
|
|
789
|
+
// appear in the output but the agent recovered and completed successfully
|
|
790
|
+
if (!outputError.detected && !streamingErrorDetected && !(exitCode === 0 && agentCompletedSuccessfully)) {
|
|
788
791
|
// Check for error type patterns in raw output (handles pretty-printed JSON)
|
|
789
792
|
const errorTypePatterns = [
|
|
790
793
|
{ pattern: '"type": "error"', type: 'AgentError' },
|
package/src/solve.mjs
CHANGED
|
@@ -1367,7 +1367,10 @@ try {
|
|
|
1367
1367
|
|
|
1368
1368
|
// Attach updated logs to PR after auto-restart completes
|
|
1369
1369
|
// Issue #1154: Skip if logs were already uploaded by verifyResults() to prevent duplicates
|
|
1370
|
-
if
|
|
1370
|
+
// Issue #1290: Always upload if auto-restart ran but last iteration's logs weren't uploaded
|
|
1371
|
+
// This ensures final logs are uploaded even when the last iteration failed
|
|
1372
|
+
const autoRestartRanButNotUploaded = watchResult?.autoRestartIterationsRan && !watchResult?.lastIterationLogUploaded;
|
|
1373
|
+
if (shouldAttachLogs && prNumber && (!logsAlreadyUploaded || autoRestartRanButNotUploaded)) {
|
|
1371
1374
|
await log('đ Uploading working session logs to Pull Request...');
|
|
1372
1375
|
try {
|
|
1373
1376
|
const logUploadSuccess = await attachLogToGitHub({
|
|
@@ -1394,8 +1397,8 @@ try {
|
|
|
1394
1397
|
} catch (uploadError) {
|
|
1395
1398
|
await log(`â ī¸ Error uploading logs: ${uploadError.message}`, { level: 'warning' });
|
|
1396
1399
|
}
|
|
1397
|
-
} else if (logsAlreadyUploaded) {
|
|
1398
|
-
await log('âšī¸ Logs already uploaded by verifyResults, skipping duplicate upload'
|
|
1400
|
+
} else if (logsAlreadyUploaded && !autoRestartRanButNotUploaded) {
|
|
1401
|
+
await log('âšī¸ Logs already uploaded by verifyResults, skipping duplicate upload');
|
|
1399
1402
|
logsAttached = true;
|
|
1400
1403
|
}
|
|
1401
1404
|
}
|
package/src/solve.watch.lib.mjs
CHANGED
|
@@ -51,6 +51,11 @@ export const watchForFeedback = async params => {
|
|
|
51
51
|
let latestSessionId = null;
|
|
52
52
|
let latestAnthropicCost = null;
|
|
53
53
|
|
|
54
|
+
// Issue #1290: Track whether auto-restart iterations actually ran and whether logs were uploaded
|
|
55
|
+
// This helps solve.mjs decide whether to upload final logs
|
|
56
|
+
let autoRestartIterationsRan = false;
|
|
57
|
+
let lastIterationLogUploaded = false;
|
|
58
|
+
|
|
54
59
|
// Track consecutive API errors for retry limit
|
|
55
60
|
const MAX_API_ERROR_RETRIES = 3;
|
|
56
61
|
let consecutiveApiErrors = 0;
|
|
@@ -168,6 +173,8 @@ export const watchForFeedback = async params => {
|
|
|
168
173
|
|
|
169
174
|
// Increment auto-restart counter and log restart number
|
|
170
175
|
autoRestartCount++;
|
|
176
|
+
autoRestartIterationsRan = true; // Issue #1290: Mark that auto-restart iterations ran
|
|
177
|
+
lastIterationLogUploaded = false; // Reset log upload tracking for new iteration
|
|
171
178
|
const restartLabel = firstIterationInTemporaryMode ? 'Initial restart' : `Restart ${autoRestartCount}/${maxAutoRestartIterations}`;
|
|
172
179
|
await log(formatAligned('đ', `${restartLabel}:`, `Running ${argv.tool.toUpperCase()} to handle uncommitted changes...`));
|
|
173
180
|
|
|
@@ -257,6 +264,60 @@ export const watchForFeedback = async params => {
|
|
|
257
264
|
currentBackoffSeconds = watchInterval;
|
|
258
265
|
await log(formatAligned('â ī¸', `${argv.tool.toUpperCase()} execution failed`, 'Will retry in next check', 2));
|
|
259
266
|
}
|
|
267
|
+
|
|
268
|
+
// Issue #1290: Upload failure logs for auto-restart iterations when --attach-logs is enabled
|
|
269
|
+
// This ensures that failed auto-restart sessions still report their logs
|
|
270
|
+
const shouldAttachLogs = argv.attachLogs || argv['attach-logs'];
|
|
271
|
+
if (isTemporaryWatch && prNumber && shouldAttachLogs) {
|
|
272
|
+
await log('');
|
|
273
|
+
await log(formatAligned('đ', 'Uploading auto-restart failure log...', ''));
|
|
274
|
+
try {
|
|
275
|
+
const logFile = getLogFile();
|
|
276
|
+
if (logFile) {
|
|
277
|
+
// Use "Auto-restart X/Y Failure Log" format to distinguish from success logs
|
|
278
|
+
const customTitle = `â ī¸ Auto-restart ${autoRestartCount}/${maxAutoRestartIterations} Failure Log`;
|
|
279
|
+
const logUploadSuccess = await attachLogToGitHub({
|
|
280
|
+
logFile,
|
|
281
|
+
targetType: 'pr',
|
|
282
|
+
targetNumber: prNumber,
|
|
283
|
+
owner,
|
|
284
|
+
repo,
|
|
285
|
+
$,
|
|
286
|
+
log,
|
|
287
|
+
sanitizeLogContent,
|
|
288
|
+
verbose: argv.verbose,
|
|
289
|
+
customTitle,
|
|
290
|
+
sessionId: toolResult.sessionId || latestSessionId,
|
|
291
|
+
tempDir,
|
|
292
|
+
// Include error information in the log upload
|
|
293
|
+
errorMessage: toolResult.errorInfo?.message || toolResult.result || `${argv.tool.toUpperCase()} execution failed`,
|
|
294
|
+
// Include pricing data if available from failed attempt
|
|
295
|
+
publicPricingEstimate: toolResult.publicPricingEstimate,
|
|
296
|
+
pricingInfo: toolResult.pricingInfo,
|
|
297
|
+
// Mark if this was a usage limit failure
|
|
298
|
+
isUsageLimit: toolResult.limitReached,
|
|
299
|
+
limitResetTime: toolResult.limitResetTime,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
if (logUploadSuccess) {
|
|
303
|
+
await log(formatAligned('', 'â
Auto-restart failure log uploaded to PR', '', 2));
|
|
304
|
+
lastIterationLogUploaded = true; // Issue #1290: Mark that logs were uploaded
|
|
305
|
+
} else {
|
|
306
|
+
await log(formatAligned('', 'â ī¸ Could not upload auto-restart failure log', '', 2));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
} catch (logUploadError) {
|
|
310
|
+
reportError(logUploadError, {
|
|
311
|
+
context: 'attach_auto_restart_failure_log',
|
|
312
|
+
prNumber,
|
|
313
|
+
owner,
|
|
314
|
+
repo,
|
|
315
|
+
autoRestartCount,
|
|
316
|
+
operation: 'upload_failure_log',
|
|
317
|
+
});
|
|
318
|
+
await log(formatAligned('', `â ī¸ Log upload error: ${cleanErrorMessage(logUploadError)}`, '', 2));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
260
321
|
} else {
|
|
261
322
|
// Success - reset error counters
|
|
262
323
|
consecutiveApiErrors = 0;
|
|
@@ -306,6 +367,7 @@ export const watchForFeedback = async params => {
|
|
|
306
367
|
|
|
307
368
|
if (logUploadSuccess) {
|
|
308
369
|
await log(formatAligned('', 'â
Auto-restart session log uploaded to PR', '', 2));
|
|
370
|
+
lastIterationLogUploaded = true; // Issue #1290: Mark that logs were uploaded
|
|
309
371
|
} else {
|
|
310
372
|
await log(formatAligned('', 'â ī¸ Could not upload auto-restart session log', '', 2));
|
|
311
373
|
}
|
|
@@ -368,9 +430,12 @@ export const watchForFeedback = async params => {
|
|
|
368
430
|
}
|
|
369
431
|
|
|
370
432
|
// Return latest session data for accurate pricing in log uploads
|
|
433
|
+
// Issue #1290: Include flags to help solve.mjs decide whether to upload final logs
|
|
371
434
|
return {
|
|
372
435
|
latestSessionId,
|
|
373
436
|
latestAnthropicCost,
|
|
437
|
+
autoRestartIterationsRan, // True if any auto-restart iterations actually ran
|
|
438
|
+
lastIterationLogUploaded, // True if the last iteration's logs were uploaded
|
|
374
439
|
};
|
|
375
440
|
};
|
|
376
441
|
|
|
@@ -440,7 +440,8 @@ export class MergeQueueProcessor {
|
|
|
440
440
|
const update = this.getProgressUpdate();
|
|
441
441
|
|
|
442
442
|
let message = `*Merge Queue*\n`;
|
|
443
|
-
|
|
443
|
+
// Issue #1292: Escape owner/repo for MarkdownV2 (may contain hyphens, underscores, etc.)
|
|
444
|
+
message += `${this.escapeMarkdown(this.owner)}/${this.escapeMarkdown(this.repo)}\n\n`;
|
|
444
445
|
|
|
445
446
|
// Progress bar in code block for better style
|
|
446
447
|
const progressBar = getProgressBar(update.progress.percentage);
|
|
@@ -515,7 +516,8 @@ export class MergeQueueProcessor {
|
|
|
515
516
|
}
|
|
516
517
|
|
|
517
518
|
let message = `${statusEmoji} *Merge Queue ${statusText}*\n`;
|
|
518
|
-
|
|
519
|
+
// Issue #1292: Escape owner/repo for MarkdownV2 (may contain hyphens, underscores, etc.)
|
|
520
|
+
message += `${this.escapeMarkdown(this.owner)}/${this.escapeMarkdown(this.repo)}\n\n`;
|
|
519
521
|
|
|
520
522
|
// Final progress bar in code block
|
|
521
523
|
const percentage = report.stats.total > 0 ? Math.round((report.stats.merged / report.stats.total) * 100) : 0;
|
package/src/usage-limit.lib.mjs
CHANGED
|
@@ -33,6 +33,7 @@ export function isUsageLimitError(message) {
|
|
|
33
33
|
const lowerMessage = message.toLowerCase();
|
|
34
34
|
|
|
35
35
|
// Check for specific usage limit patterns
|
|
36
|
+
// Supports both plain string patterns (checked via .includes()) and RegExp patterns
|
|
36
37
|
const patterns = [
|
|
37
38
|
// Generic
|
|
38
39
|
"you've hit your usage limit",
|
|
@@ -52,12 +53,21 @@ export function isUsageLimitError(message) {
|
|
|
52
53
|
'billing hard limit',
|
|
53
54
|
'please try again at', // Codex/OpenCode style
|
|
54
55
|
'available again at',
|
|
55
|
-
|
|
56
|
+
// Claude shows: "â resets 5am" or "resets Jan 15, 8am"
|
|
57
|
+
// Issue #1290: Use regex to avoid false positives when "resets" appears in code output
|
|
58
|
+
// (e.g., "loads a shell and resets", "Also resets drag start")
|
|
59
|
+
// Only match "resets" when followed by a time-like pattern (digit or month name)
|
|
60
|
+
/resets\s+(?:(?:at\s+)?[0-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))/i,
|
|
56
61
|
// Agent/OpenCode Zen specific - issue #1287
|
|
57
62
|
'freeusagelimiterror', // Agent JSON error type
|
|
58
63
|
];
|
|
59
64
|
|
|
60
|
-
return patterns.some(pattern =>
|
|
65
|
+
return patterns.some(pattern => {
|
|
66
|
+
if (pattern instanceof RegExp) {
|
|
67
|
+
return pattern.test(lowerMessage);
|
|
68
|
+
}
|
|
69
|
+
return lowerMessage.includes(pattern);
|
|
70
|
+
});
|
|
61
71
|
}
|
|
62
72
|
|
|
63
73
|
/**
|