@link-assistant/hive-mind 1.35.3 → 1.35.4

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,11 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.35.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 0df2139: Harden Telegram message formatting: escape special characters in user mentions, options text, and server overrides. Add safeReply with plain text fallback and diagnostic logging when Telegram rejects Markdown. Improve error messages with user identity context for root cause analysis.
8
+
3
9
  ## 1.35.3
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.35.3",
3
+ "version": "1.35.4",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -40,9 +40,12 @@ export function buildUserMention({ user, id: idParam, username: usernameParam, f
40
40
  const link = username ? `https://t.me/${username}` : `tg://user?id=${id}`;
41
41
 
42
42
  switch (parseMode) {
43
- case 'Markdown':
43
+ case 'Markdown': {
44
44
  // Legacy Markdown: [text](url)
45
- return `[${displayName}](${link})`;
45
+ // Escape _ and * in display name to prevent "can't find end of entity" errors (issue #1460)
46
+ const escapedMarkdownName = displayName.replace(/_/g, '\\_').replace(/\*/g, '\\*');
47
+ return `[${escapedMarkdownName}](${link})`;
48
+ }
46
49
  case 'MarkdownV2': {
47
50
  // MarkdownV2 requires escaping special characters
48
51
  const escapedName = displayName.replace(/([_*[\]()~`>#+\-=|{}.!])/g, '\\$1');
@@ -992,9 +992,11 @@ async function handleSolveCommand(ctx) {
992
992
 
993
993
  const requester = buildUserMention({ user: ctx.from, parseMode: 'Markdown' });
994
994
  // Issue #1228: Show only user-provided options (exclude locked overrides to avoid duplication)
995
- const userOptionsText = userArgs.slice(1).join(' ') || 'none';
996
- let infoBlock = `Requested by: ${requester}\nURL: ${escapeMarkdown(normalizedUrl)}\n\nšŸ›  Options: ${userOptionsText}`;
997
- if (solveOverrides.length > 0) infoBlock += `\nšŸ”’ Locked options: ${solveOverrides.join(' ')}`;
995
+ // Issue #1460: Escape options text to prevent Markdown parsing errors
996
+ const userOptionsRaw = userArgs.slice(1).join(' ');
997
+ let infoBlock = `Requested by: ${requester}\nURL: ${escapeMarkdown(normalizedUrl)}`;
998
+ if (userOptionsRaw) infoBlock += `\n\nšŸ›  Options: ${escapeMarkdown(userOptionsRaw)}`;
999
+ if (solveOverrides.length > 0) infoBlock += `${userOptionsRaw ? '\n' : '\n\n'}šŸ”’ Locked options: ${escapeMarkdown(solveOverrides.join(' '))}`;
998
1000
  const solveQueue = getSolveQueue({ verbose: VERBOSE });
999
1001
 
1000
1002
  // Check for duplicate URL in queue
@@ -1162,10 +1164,12 @@ async function handleHiveCommand(ctx) {
1162
1164
  const requester = buildUserMention({ user: ctx.from, parseMode: 'Markdown' });
1163
1165
  const escapedUrl = escapeMarkdown(args[0]);
1164
1166
  // Issue #1228: Show only user-provided options (exclude locked overrides to avoid duplication)
1165
- const userOptionsText = normalizedArgs.slice(1).join(' ') || 'none';
1166
- let infoBlock = `Requested by: ${requester}\nURL: ${escapedUrl}\n\nšŸ›  Options: ${userOptionsText}`;
1167
+ // Issue #1460: Escape options text to prevent Markdown parsing errors
1168
+ const userOptionsRaw = normalizedArgs.slice(1).join(' ');
1169
+ let infoBlock = `Requested by: ${requester}\nURL: ${escapedUrl}`;
1170
+ if (userOptionsRaw) infoBlock += `\n\nšŸ›  Options: ${escapeMarkdown(userOptionsRaw)}`;
1167
1171
  if (hiveOverrides.length > 0) {
1168
- infoBlock += `\nšŸ”’ Locked options: ${hiveOverrides.join(' ')}`;
1172
+ infoBlock += `${userOptionsRaw ? '\n' : '\n\n'}šŸ”’ Locked options: ${escapeMarkdown(hiveOverrides.join(' '))}`;
1169
1173
  }
1170
1174
 
1171
1175
  const startingMessage = await ctx.reply(`šŸš€ Starting hive command...\n\n${infoBlock}`, { parse_mode: 'Markdown', reply_to_message_id: ctx.message.message_id });
@@ -1305,18 +1309,22 @@ bot.catch((error, ctx) => {
1305
1309
  let errorMessage;
1306
1310
 
1307
1311
  if (isTelegramParsingError) {
1308
- // Special handling for Telegram API parsing errors caused by unescaped special characters
1309
- errorMessage = `āŒ A message formatting error occurred.\n\nšŸ’” This usually means there was a problem with special characters in the response.\nPlease try your command again with a different URL or contact support.`;
1310
- // Show the user's input with special characters visible (if available)
1312
+ // Issue #1460: Log detailed context for root cause analysis (always logged, not just in verbose mode)
1313
+ const userInfo = ctx.from ? { id: ctx.from.id, username: ctx.from.username, first_name: ctx.from.first_name, last_name: ctx.from.last_name } : 'unknown';
1314
+ console.error(`[telegram-bot] Parsing error: ${error.message}`);
1315
+ console.error(`[telegram-bot] Parsing error context - user: ${JSON.stringify(userInfo)}, command: ${ctx.message?.text?.split(' ')[0] || 'unknown'}`);
1316
+ console.error(`[telegram-bot] User input text: ${ctx.message?.text || 'none'}`);
1311
1317
  if (ctx.message?.text) {
1318
+ const visibleInput = makeSpecialCharsVisible(ctx.message.text, { maxLength: 500 });
1319
+ console.error(`[telegram-bot] User input (special chars visible): ${visibleInput}`);
1312
1320
  const cleanedInput = cleanNonPrintableChars(ctx.message.text);
1313
- const visibleInput = makeSpecialCharsVisible(cleanedInput, { maxLength: 150 });
1314
- if (visibleInput !== cleanedInput) errorMessage += `\n\nšŸ“ Your input (with special chars visible):\n\`${escapeMarkdown(visibleInput)}\``;
1315
- }
1316
- if (VERBOSE) {
1317
- const escapedError = escapeMarkdown(error.message || 'Unknown error');
1318
- errorMessage += `\n\nšŸ” Debug info: ${escapedError}\nUpdate ID: ${ctx.update.update_id}`;
1321
+ if (cleanedInput !== ctx.message.text) {
1322
+ console.error(`[telegram-bot] ${ctx.message.text.length - cleanedInput.length} hidden character(s) detected in input`);
1323
+ }
1319
1324
  }
1325
+
1326
+ // Issue #1460: Show user a simple, non-confusing message — all details are in the logs
1327
+ 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}`;
1320
1328
  } else {
1321
1329
  // Build informative error message for other errors
1322
1330
  errorMessage = 'āŒ An error occurred while processing your request.\n\n';
@@ -1334,14 +1342,21 @@ bot.catch((error, ctx) => {
1334
1342
  if (VERBOSE) errorMessage += `\n\nšŸ” Debug info: Update ID: ${ctx.update.update_id}`;
1335
1343
  }
1336
1344
 
1337
- ctx.reply(errorMessage, { parse_mode: 'Markdown' }).catch(replyError => {
1338
- console.error('Failed to send error message to user:', replyError);
1339
- // Try sending a simple text message without Markdown if Markdown parsing failed
1340
- const plainMessage = `An error occurred while processing your request. Please try again or contact support.\n\nError: ${error.message || 'Unknown error'}`;
1341
- ctx.reply(plainMessage).catch(fallbackError => {
1342
- console.error('Failed to send fallback error message:', fallbackError);
1345
+ // Issue #1460: For parsing errors, always send as plain text (we already know Markdown is the problem)
1346
+ // For other errors, try Markdown first, then fall back to plain text
1347
+ if (isTelegramParsingError) {
1348
+ ctx.reply(errorMessage).catch(fallbackError => {
1349
+ console.error('Failed to send plain text error message:', fallbackError);
1343
1350
  });
1344
- });
1351
+ } else {
1352
+ ctx.reply(errorMessage, { parse_mode: 'Markdown' }).catch(replyError => {
1353
+ console.error('Failed to send error message to user:', replyError);
1354
+ const plainMessage = `An error occurred while processing your request. Please try again or contact support.\n\nError: ${error.message || 'Unknown error'}`;
1355
+ ctx.reply(plainMessage).catch(fallbackError => {
1356
+ console.error('Failed to send fallback error message:', fallbackError);
1357
+ });
1358
+ });
1359
+ }
1345
1360
  }
1346
1361
  });
1347
1362