@link-assistant/hive-mind 1.69.15 → 1.69.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.69.17
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 46422f1: Increase the default `HIVE_MIND_USAGE_API_CACHE_TTL_MS` from 10 → 13 minutes so the Claude Usage API (`/api/oauth/usage`) is queried less frequently and we stop tripping the upstream "Resets in 3m Xs" rate-limit message. Operators can still override the value via the environment variable. Resolves #1798.
|
|
8
|
+
|
|
9
|
+
## 1.69.16
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- ca0d938: Fix Telegram work-session completion failing with "Bad Request: can't parse entities" when the discovered Pull request URL contained Markdown-significant characters (`_`, `*`, `` ` ``, `[`). `appendPullRequestLine` (issue #1688) inserted the raw URL into a Markdown message even though the surrounding `Issue:` line was already escaped by `buildTelegramInfoBlock`, so a repo slug like `save_visiogetbb/pull/8` opened an italic entity at byte offset 318 that never closed. The appended `Pull request:` line is now passed through `escapeMarkdown`, and `safeReply`/`safeEditMessageText`/`installTelegramFormattingFallback` now log the offending byte-offset window and the plain-text fallback under `--verbose` so future parse errors point straight to the unescaped character. Resolves #1801.
|
|
14
|
+
|
|
3
15
|
## 1.69.15
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
package/src/config.lib.mjs
CHANGED
|
@@ -528,12 +528,14 @@ export const getClaudeEnv = (options = {}) => {
|
|
|
528
528
|
// Cache TTL configurations (in milliseconds)
|
|
529
529
|
// The Usage API (Claude limits) has stricter rate limiting than regular APIs
|
|
530
530
|
// See: https://github.com/link-assistant/hive-mind/issues/1074
|
|
531
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1798
|
|
531
532
|
export const cacheTtl = {
|
|
532
533
|
// General API cache TTL (GitHub API, etc.)
|
|
533
534
|
api: parseIntWithDefault('HIVE_MIND_API_CACHE_TTL_MS', 3 * 60 * 1000), // 3 minutes
|
|
534
|
-
// Claude Usage API cache TTL -
|
|
535
|
-
//
|
|
536
|
-
|
|
535
|
+
// Claude Usage API cache TTL - increased by 3 minutes (from 10 → 13) per issue #1798
|
|
536
|
+
// because users still hit "Resets in 3m xs" rate-limit responses. The API
|
|
537
|
+
// returns null values or 429 when called too frequently.
|
|
538
|
+
usageApi: parseIntWithDefault('HIVE_MIND_USAGE_API_CACHE_TTL_MS', 13 * 60 * 1000), // 13 minutes
|
|
537
539
|
// System metrics cache TTL (RAM, CPU, disk)
|
|
538
540
|
system: parseIntWithDefault('HIVE_MIND_SYSTEM_CACHE_TTL_MS', 2 * 60 * 1000), // 2 minutes
|
|
539
541
|
};
|
package/src/limits.lib.mjs
CHANGED
|
@@ -1271,17 +1271,20 @@ export function formatCodexLimitsSection(codexLimits, codexError = null, options
|
|
|
1271
1271
|
* Values are loaded from config.lib.mjs which supports environment variable overrides.
|
|
1272
1272
|
*
|
|
1273
1273
|
* IMPORTANT: The Claude Usage API has stricter rate limiting than regular APIs.
|
|
1274
|
-
* Calling it
|
|
1274
|
+
* Calling it too frequently may return null values or a 429 "Resets in Xm Xs" error.
|
|
1275
|
+
* Default raised from 10 → 13 minutes in issue #1798 (the previous 10-minute TTL still
|
|
1276
|
+
* occasionally tripped a ~3-minute rate-limit window).
|
|
1275
1277
|
* See: https://github.com/link-assistant/hive-mind/issues/1074
|
|
1278
|
+
* See: https://github.com/link-assistant/hive-mind/issues/1798
|
|
1276
1279
|
*
|
|
1277
1280
|
* Configurable via environment variables:
|
|
1278
1281
|
* - HIVE_MIND_API_CACHE_TTL_MS: General API cache TTL (default: 180000 = 3 minutes)
|
|
1279
|
-
* - HIVE_MIND_USAGE_API_CACHE_TTL_MS: Claude Usage API cache TTL (default:
|
|
1282
|
+
* - HIVE_MIND_USAGE_API_CACHE_TTL_MS: Claude Usage API cache TTL (default: 780000 = 13 minutes)
|
|
1280
1283
|
* - HIVE_MIND_SYSTEM_CACHE_TTL_MS: System metrics cache TTL (default: 120000 = 2 minutes)
|
|
1281
1284
|
*/
|
|
1282
1285
|
export const CACHE_TTL = {
|
|
1283
1286
|
API: cacheTtl.api, // 3 minutes for regular API calls (GitHub)
|
|
1284
|
-
USAGE_API: cacheTtl.usageApi, //
|
|
1287
|
+
USAGE_API: cacheTtl.usageApi, // 13 minutes for Claude Usage API (rate limited)
|
|
1285
1288
|
SYSTEM: cacheTtl.system, // 2 minutes for system metrics (RAM, CPU, disk)
|
|
1286
1289
|
};
|
|
1287
1290
|
|
|
@@ -1345,9 +1348,10 @@ export function resetLimitCache() {
|
|
|
1345
1348
|
|
|
1346
1349
|
export async function getCachedClaudeLimits(verbose = false) {
|
|
1347
1350
|
const cache = getLimitCache();
|
|
1348
|
-
// Use USAGE_API TTL (
|
|
1349
|
-
// The Claude Usage API returns null values when called too frequently
|
|
1351
|
+
// Use USAGE_API TTL (13 min by default, see issue #1798) for Claude limits to avoid rate limiting.
|
|
1352
|
+
// The Claude Usage API returns null values or 429 errors when called too frequently.
|
|
1350
1353
|
// See: https://github.com/link-assistant/hive-mind/issues/1074
|
|
1354
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1798
|
|
1351
1355
|
const cached = cache.get('claude', CACHE_TTL.USAGE_API);
|
|
1352
1356
|
if (cached) {
|
|
1353
1357
|
if (verbose) console.log('[VERBOSE] /limits-cache: Using cached Claude limits (TTL: ' + Math.round(CACHE_TTL.USAGE_API / 60000) + ' minutes)');
|
|
@@ -1365,8 +1369,9 @@ export async function getCachedClaudeLimits(verbose = false) {
|
|
|
1365
1369
|
cache.set('claude', result, CACHE_TTL.USAGE_API);
|
|
1366
1370
|
} else if (result.error && result.error.includes('Rate limited')) {
|
|
1367
1371
|
// Cache rate-limit errors to prevent hammering the API
|
|
1368
|
-
// Use the same
|
|
1372
|
+
// Use the same USAGE_API TTL (13 min by default) as successful responses
|
|
1369
1373
|
// See: https://github.com/link-assistant/hive-mind/issues/1446
|
|
1374
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1798
|
|
1370
1375
|
cache.set('claude-rate-limited', result, CACHE_TTL.USAGE_API);
|
|
1371
1376
|
if (verbose) console.log('[VERBOSE] /limits-cache: Cached rate-limit error for ' + Math.round(CACHE_TTL.USAGE_API / 60000) + ' minutes');
|
|
1372
1377
|
}
|
|
@@ -49,11 +49,30 @@ export function buildTelegramFormattingFallbackText(text, options = {}) {
|
|
|
49
49
|
return `${getFormattingFallbackWarning(locale)}\n\n${stripTelegramMarkdown(text)}`;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function logFormattingFailure(scope, error, text, verbose = false) {
|
|
52
|
+
function logFormattingFailure(scope, error, text, verbose = false, fallbackText = null) {
|
|
53
53
|
const message = error?.description || error?.message || String(error || '');
|
|
54
54
|
console.error(`[telegram-bot] ${scope}: formatted Telegram message failed: ${message}`);
|
|
55
55
|
if (verbose) {
|
|
56
|
-
|
|
56
|
+
const originalBytes = Buffer.byteLength(String(text ?? ''), 'utf-8');
|
|
57
|
+
console.error(`[telegram-bot] ${scope}: Failing message (${originalBytes} bytes): ${text}`);
|
|
58
|
+
// Issue #1801: when the parser rejects an entity, surface the byte offset
|
|
59
|
+
// from the error along with a small window of the message around it to
|
|
60
|
+
// make pinpointing the offending character trivial on the next iteration.
|
|
61
|
+
const offsetMatch = /byte offset (\d+)/i.exec(message);
|
|
62
|
+
if (offsetMatch) {
|
|
63
|
+
const offset = Number(offsetMatch[1]);
|
|
64
|
+
const buf = Buffer.from(String(text ?? ''), 'utf-8');
|
|
65
|
+
const start = Math.max(0, offset - 32);
|
|
66
|
+
const end = Math.min(buf.length, offset + 32);
|
|
67
|
+
// Decode the window; replacement character is used for bytes that fall
|
|
68
|
+
// mid-codepoint so we still print *something* useful.
|
|
69
|
+
const window = buf.slice(start, end).toString('utf-8');
|
|
70
|
+
console.error(`[telegram-bot] ${scope}: Byte offset ${offset} context [${start}..${end}]: ${JSON.stringify(window)}`);
|
|
71
|
+
}
|
|
72
|
+
if (fallbackText !== null) {
|
|
73
|
+
const fallbackBytes = Buffer.byteLength(String(fallbackText ?? ''), 'utf-8');
|
|
74
|
+
console.error(`[telegram-bot] ${scope}: Fallback message (${fallbackBytes} bytes): ${fallbackText}`);
|
|
75
|
+
}
|
|
57
76
|
}
|
|
58
77
|
}
|
|
59
78
|
|
|
@@ -65,8 +84,8 @@ export async function safeReply(ctx, text, options = {}) {
|
|
|
65
84
|
return await ctx.reply(text, firstOptions);
|
|
66
85
|
} catch (error) {
|
|
67
86
|
if (!isTelegramFormattingError(error)) throw error;
|
|
68
|
-
logFormattingFailure('safeReply', error, text, verbose);
|
|
69
87
|
const fallbackText = buildTelegramFormattingFallbackText(text, { fallbackLocale });
|
|
88
|
+
logFormattingFailure('safeReply', error, text, verbose, fallbackText);
|
|
70
89
|
return await ctx.reply(fallbackText, { ...telegramOptions, parse_mode: undefined });
|
|
71
90
|
}
|
|
72
91
|
}
|
|
@@ -78,8 +97,8 @@ export async function safeEditMessageText(telegram, chatId, messageId, inlineMes
|
|
|
78
97
|
return await telegram.editMessageText(chatId, messageId, inlineMessageId, text, firstOptions);
|
|
79
98
|
} catch (error) {
|
|
80
99
|
if (!isTelegramFormattingError(error)) throw error;
|
|
81
|
-
logFormattingFailure('safeEditMessageText', error, text, verbose);
|
|
82
100
|
const fallbackText = buildTelegramFormattingFallbackText(text, { fallbackLocale });
|
|
101
|
+
logFormattingFailure('safeEditMessageText', error, text, verbose, fallbackText);
|
|
83
102
|
return await telegram.editMessageText(chatId, messageId, inlineMessageId, fallbackText, { ...telegramOptions, parse_mode: undefined });
|
|
84
103
|
}
|
|
85
104
|
}
|
|
@@ -98,9 +117,10 @@ function wrapTelegramMethod(telegram, methodName, textIndex, optionsIndex, defau
|
|
|
98
117
|
return await original.apply(this, args);
|
|
99
118
|
} catch (error) {
|
|
100
119
|
if (!isTelegramFormattingError(error) || typeof text !== 'string') throw error;
|
|
101
|
-
|
|
120
|
+
const fallbackText = buildTelegramFormattingFallbackText(text, { fallbackLocale: fallbackLocale || defaults.fallbackLocale });
|
|
121
|
+
logFormattingFailure(methodName, error, text, verbose || defaults.verbose, fallbackText);
|
|
102
122
|
const retryArgs = [...args];
|
|
103
|
-
retryArgs[textIndex] =
|
|
123
|
+
retryArgs[textIndex] = fallbackText;
|
|
104
124
|
retryArgs[optionsIndex] = { ...telegramOptions, parse_mode: undefined };
|
|
105
125
|
return await original.apply(this, retryArgs);
|
|
106
126
|
}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* the args before they are forwarded to /solve, /hive (or /task --split). When
|
|
6
6
|
* set, the bot:
|
|
7
7
|
* 1. Fetches usage limits for the selected tool (Claude or Codex) using the
|
|
8
|
-
* shared cached helpers in limits.lib.mjs (TTL:
|
|
9
|
-
*
|
|
8
|
+
* shared cached helpers in limits.lib.mjs (USAGE_API TTL: 13 minutes by
|
|
9
|
+
* default — see issue #1798 — to avoid rate limiting).
|
|
10
10
|
* 2. Embeds a compact "Limits at start" snapshot below the infoBlock so the
|
|
11
11
|
* starting/executing message shows the user how much budget they had at
|
|
12
12
|
* the moment the command was queued.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t } from './i18n.lib.mjs';
|
|
2
|
+
import { escapeMarkdown } from './telegram-markdown.lib.mjs';
|
|
2
3
|
|
|
3
4
|
const FAILURE_STATUSES = new Set(['failed', 'cancelled', 'canceled', 'error']);
|
|
4
5
|
|
|
@@ -74,7 +75,7 @@ export function formatExecutingWorkSessionMessage({ sessionName = 'unknown', iso
|
|
|
74
75
|
*/
|
|
75
76
|
export function appendPullRequestLine(infoBlock, pullRequestUrl, { locale = null } = {}) {
|
|
76
77
|
if (!pullRequestUrl || !infoBlock) return infoBlock || '';
|
|
77
|
-
if (infoBlock.includes(pullRequestUrl)) return infoBlock;
|
|
78
|
+
if (infoBlock.includes(pullRequestUrl) || infoBlock.includes(escapeMarkdown(pullRequestUrl))) return infoBlock;
|
|
78
79
|
|
|
79
80
|
const lines = infoBlock.split('\n');
|
|
80
81
|
let lastUrlLineIdx = -1;
|
|
@@ -89,7 +90,10 @@ export function appendPullRequestLine(infoBlock, pullRequestUrl, { locale = null
|
|
|
89
90
|
lastUrlLineIdx = i;
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
+
// Issue #1801: escape underscores/asterisks so Markdown parser doesn't open
|
|
94
|
+
// an entity on URLs like .../save_visiogetbb/pull/8 that the Issue: line
|
|
95
|
+
// above already had escaped at buildTelegramInfoBlock time.
|
|
96
|
+
const prLine = `${text(locale, 'telegram.info_pull_request_label', 'Pull request')}: ${escapeMarkdown(pullRequestUrl)}`;
|
|
93
97
|
if (lastUrlLineIdx === -1) {
|
|
94
98
|
return `${infoBlock}\n${prLine}`;
|
|
95
99
|
}
|