@link-assistant/hive-mind 2.11.13 → 2.12.1

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +4 -1
  3. package/src/agent-command.lib.mjs +74 -0
  4. package/src/agent.lib.mjs +59 -34
  5. package/src/agentic-cli-updater.lib.mjs +241 -0
  6. package/src/claude.connection.lib.mjs +209 -0
  7. package/src/claude.lib.mjs +6 -202
  8. package/src/codex.lib.mjs +0 -128
  9. package/src/formal-ai-isolation.lib.mjs +62 -0
  10. package/src/formal-ai-maintenance.lib.mjs +106 -0
  11. package/src/formal-ai-model.lib.mjs +25 -0
  12. package/src/formal-ai-runtime.lib.mjs +10 -0
  13. package/src/formal-ai-sidecar.lib.mjs +565 -0
  14. package/src/formal-ai-updater.lib.mjs +294 -0
  15. package/src/formal-ai-version.lib.mjs +100 -0
  16. package/src/formal-ai.lib.mjs +11 -16
  17. package/src/github-rate-limit.lib.mjs +3 -0
  18. package/src/github-url-parser.lib.mjs +255 -0
  19. package/src/github.lib.mjs +22 -343
  20. package/src/hive.mjs +0 -152
  21. package/src/interactive-mode.lib.mjs +0 -43
  22. package/src/isolation-runner.lib.mjs +44 -173
  23. package/src/limits.lib.mjs +0 -89
  24. package/src/model-args.lib.mjs +32 -0
  25. package/src/models/index.mjs +5 -19
  26. package/src/session-monitor.lib.mjs +14 -172
  27. package/src/solve.auto-merge.lib.mjs +70 -164
  28. package/src/solve.mjs +31 -193
  29. package/src/solve.repository.lib.mjs +0 -83
  30. package/src/solve.results.lib.mjs +2 -92
  31. package/src/solve.session.lib.mjs +52 -19
  32. package/src/solve.tool-uncommitted.lib.mjs +22 -0
  33. package/src/state-lock.lib.mjs +82 -0
  34. package/src/telegram-bot.mjs +17 -65
  35. package/src/telegram-fix-command.lib.mjs +1 -8
  36. package/src/telegram-merge-queue.lib.mjs +3 -155
  37. package/src/telegram-solve-queue.lib.mjs +9 -168
  38. package/src/telegram-task-command.lib.mjs +1 -8
  39. package/src/use-m-bootstrap.lib.mjs +6 -5
  40. package/src/use-with-retry.lib.mjs +128 -2
  41. package/src/working-session-summary.lib.mjs +47 -1
@@ -33,7 +33,6 @@
33
33
  * @module interactive-mode.lib.mjs
34
34
  * @experimental
35
35
  */
36
-
37
36
  import { CONFIG, createCollapsible, createRawJsonSection, createRedactedRawJsonSection, escapeMarkdown, execFileAsync, formatCost, formatDuration, getToolIcon, safeJsonStringify, sanitizeUnicode, truncateMiddle } from './interactive-mode.shared.lib.mjs';
38
37
  import { createCodexEventHandlers } from './interactive-codex-events.lib.mjs';
39
38
  import { createSystemLifecycleHandlers } from './interactive-system-events.lib.mjs';
@@ -116,7 +115,6 @@ export const createInteractiveHandler = options => {
116
115
  editsSucceeded: 0,
117
116
  editsFailed: 0,
118
117
  };
119
-
120
118
  const imageRenderer = createImageRenderer({ owner, repo, prNumber, mediaRef, log, verbose, execFile: execFileFn, enabled: imageUploadEnabled, uploader: injectedImageUploader, state }); // Issue #1843
121
119
 
122
120
  /**
@@ -141,7 +139,6 @@ export const createInteractiveHandler = options => {
141
139
  await log(`⚠️ Interactive mode: getAllKnownLocalTokens failed: ${err.message}`, { verbose: true });
142
140
  }
143
141
  }
144
-
145
142
  let hits;
146
143
  try {
147
144
  hits = await containsKnownToken(body, knownTokens);
@@ -177,7 +174,6 @@ export const createInteractiveHandler = options => {
177
174
  throw error;
178
175
  }
179
176
  };
180
-
181
177
  /**
182
178
  * Post a comment to the PR (with rate limiting)
183
179
  * @param {string} body - Comment body
@@ -201,7 +197,6 @@ export const createInteractiveHandler = options => {
201
197
 
202
198
  const now = Date.now();
203
199
  const timeSinceLastComment = now - state.lastCommentTime;
204
-
205
200
  if (timeSinceLastComment < CONFIG.MIN_COMMENT_INTERVAL) {
206
201
  // Queue the comment for later with toolId/taskId for tracking
207
202
  state.commentQueue.push({ body: safeBody, toolId, taskId, onPosted });
@@ -240,7 +235,6 @@ export const createInteractiveHandler = options => {
240
235
  // set so --auto-attach-solution-summary correctly excludes it from the
241
236
  // AI-authored-comment check. Tracking is a no-op when commentId is null.
242
237
  trackToolCommentId(commentId);
243
-
244
238
  if (commentId && typeof onPosted === 'function') {
245
239
  try {
246
240
  await onPosted(commentId);
@@ -277,7 +271,6 @@ export const createInteractiveHandler = options => {
277
271
  }
278
272
  return false;
279
273
  }
280
-
281
274
  // Issue #1745: sanitize before sending. editComment is the path that
282
275
  // leaked TELEGRAM_BOT_TOKEN in xlab2016/space_db_private#20.
283
276
  const safeBody = await sanitizeAndWarn(body);
@@ -317,7 +310,6 @@ export const createInteractiveHandler = options => {
317
310
  }
318
311
 
319
312
  state.isProcessing = true;
320
-
321
313
  while (state.commentQueue.length > 0) {
322
314
  const now = Date.now();
323
315
  const timeSinceLastComment = now - state.lastCommentTime;
@@ -332,7 +324,6 @@ export const createInteractiveHandler = options => {
332
324
  const { body, toolId, taskId, onPosted } = queueItem;
333
325
  // Post the comment (don't pass toolId/taskId to avoid re-queueing)
334
326
  const commentId = await postComment(body, null, null, onPosted);
335
-
336
327
  // If this was a tool use comment, update the pending call with the comment ID
337
328
  if (toolId && commentId) {
338
329
  const pendingCall = state.pendingToolCalls.get(toolId);
@@ -388,7 +379,6 @@ export const createInteractiveHandler = options => {
388
379
  }
389
380
  return;
390
381
  }
391
-
392
382
  state.sessionId = data.session_id;
393
383
  state.startTime = Date.now();
394
384
 
@@ -400,7 +390,6 @@ export const createInteractiveHandler = options => {
400
390
  const mcpServersList = formatInteractiveMcpServersList(mcpServers);
401
391
  const mcpDiagnostics = getInteractiveMcpDiagnostics(mcpServers, tools);
402
392
  const mcpDiagnosticsBlock = mcpDiagnostics.length > 0 ? `\n${mcpDiagnostics.map(message => `> ${message}`).join('\n')}\n` : '';
403
-
404
393
  // Format slash commands
405
394
  const slashCommands = data.slash_commands || [];
406
395
  const slashCommandsList = slashCommands.length > 0 ? slashCommands.map(c => `\`/${c}\``).join(', ') : '_None_';
@@ -429,7 +418,6 @@ ${mcpDiagnosticsBlock}
429
418
  ${createRawJsonSection(data)}`;
430
419
 
431
420
  await postComment(comment);
432
-
433
421
  if (verbose) {
434
422
  await log(`🔌 Interactive mode: Session initialized (${state.sessionId})`, { verbose: true });
435
423
  }
@@ -458,7 +446,6 @@ ${createRawJsonSection(data)}`;
458
446
  ${createRawJsonSection(data)}`;
459
447
 
460
448
  await postComment(comment);
461
-
462
449
  if (verbose) {
463
450
  await log(`💬 Interactive mode: Assistant text (${text.length} chars)`, { verbose: true });
464
451
  }
@@ -478,7 +465,6 @@ ${createRawJsonSection(data)}`;
478
465
 
479
466
  // Register this tool use for potential standalone result rendering
480
467
  state.toolUseRegistry.set(toolId, { toolName, toolIcon });
481
-
482
468
  // Format tool input based on tool type
483
469
  let inputDisplay;
484
470
  const input = toolUse.input || {};
@@ -548,7 +534,6 @@ ${createRawJsonSection(data)}`;
548
534
  const KEEP_START = 15;
549
535
  const KEEP_END = 15;
550
536
  const skipped = todos.length - KEEP_START - KEEP_END;
551
-
552
537
  const startTodos = todos.slice(0, KEEP_START).map(t => `- [${t.status === 'completed' ? 'x' : ' '}] ${t.content}`);
553
538
  const endTodos = todos.slice(-KEEP_END).map(t => `- [${t.status === 'completed' ? 'x' : ' '}] ${t.content}`);
554
539
 
@@ -605,7 +590,6 @@ ${createRawJsonSection(data)}`;
605
590
  toolName,
606
591
  toolIcon,
607
592
  });
608
-
609
593
  // Post the comment, passing toolId for queue tracking
610
594
  const commentId = await postComment(comment, toolId);
611
595
 
@@ -633,7 +617,6 @@ ${createRawJsonSection(data)}`;
633
617
  */
634
618
  const handleToolResult = async (data, toolResult) => {
635
619
  state.toolResultCount++;
636
-
637
620
  const toolUseId = toolResult.tool_use_id || 'unknown';
638
621
  const isError = toolResult.is_error || false;
639
622
  const statusIcon = isError ? '❌' : '✅';
@@ -661,7 +644,6 @@ ${createRawJsonSection(data)}`;
661
644
  keepStart: 25,
662
645
  keepEnd: 25,
663
646
  });
664
-
665
647
  // Issue #1843: render images this result read/produced (used by both paths).
666
648
  const imagesSection = await imageRenderer.section([toolResult.content, data.tool_use_result], imageRenderer.toolLabel(toolUseId));
667
649
  const imagesBlock = imagesSection ? `${imagesSection}\n\n` : '';
@@ -693,7 +675,6 @@ ${createRawJsonSection(data)}`;
693
675
  await processQueue();
694
676
  state.isProcessing = wasProcessing;
695
677
  }
696
-
697
678
  // Check again after queue flush
698
679
  commentId = pendingCall.commentId;
699
680
 
@@ -716,7 +697,6 @@ ${createRawJsonSection(data)}`;
716
697
  }
717
698
  }
718
699
  }
719
-
720
700
  if (commentId) {
721
701
  // Create merged comment with both call and result
722
702
  const mergedComment = `## ${toolIcon} ${toolName} tool use
@@ -748,7 +728,6 @@ ${createRedactedRawJsonSection([toolData, data])}`;
748
728
  });
749
729
  }
750
730
  }
751
-
752
731
  // Clean up the pending call since we're posting separately
753
732
  state.pendingToolCalls.delete(toolUseId);
754
733
  }
@@ -769,7 +748,6 @@ ${imagesBlock}---
769
748
  ${createRedactedRawJsonSection(data)}`;
770
749
 
771
750
  await postComment(comment);
772
-
773
751
  if (verbose) {
774
752
  const contentLength = content.length;
775
753
  await log(`📋 Interactive mode: Tool result posted as separate comment (${contentLength} chars)`, {
@@ -799,7 +777,6 @@ ${createRedactedRawJsonSection(data)}`;
799
777
  let statsTable = '| Metric | Value |\n|--------|-------|\n';
800
778
  statsTable += `| **Status** | ${statusText} |\n`;
801
779
  statsTable += `| **Session ID** | \`${data.session_id || 'unknown'}\` |\n`;
802
-
803
780
  if (data.duration_ms) {
804
781
  statsTable += `| **Duration** | ${formatDuration(data.duration_ms)} |\n`;
805
782
  }
@@ -856,7 +833,6 @@ ${createRawJsonSection(data)}`;
856
833
  await log(`🏁 Interactive mode: Session ${statusText.toLowerCase()}`, { verbose: true });
857
834
  }
858
835
  };
859
-
860
836
  /**
861
837
  * Handle system.task_started event (Agent subtask started)
862
838
  * Creates a progress comment that will be updated by task_progress events
@@ -895,7 +871,6 @@ ${promptSection}
895
871
  ---
896
872
 
897
873
  ${createRawJsonSection(data)}`;
898
-
899
874
  // Track this task BEFORE posting
900
875
  state.pendingTasks.set(taskId, {
901
876
  commentId: null,
@@ -923,7 +898,6 @@ ${createRawJsonSection(data)}`;
923
898
  await log(`🤖 Interactive mode: Agent task started - ${description} (task: ${taskId})`, { verbose: true });
924
899
  }
925
900
  };
926
-
927
901
  /**
928
902
  * Handle system.task_progress event (Agent subtask progress update)
929
903
  * Updates the existing task comment instead of creating a new one
@@ -941,7 +915,6 @@ ${createRawJsonSection(data)}`;
941
915
  pendingTask.progressCount++;
942
916
  pendingTask.lastProgressDescription = description;
943
917
  pendingTask.allEvents.push(data);
944
-
945
918
  let commentId = pendingTask.commentId;
946
919
 
947
920
  // Wait for comment ID if not yet available — flush queue first to avoid timeout
@@ -969,7 +942,6 @@ ${createRawJsonSection(data)}`;
969
942
  return `- 🔀 ${agentTag} ${toolIcon} ${e.description || 'Working...'}`;
970
943
  })
971
944
  .join('\n');
972
-
973
945
  const durationText = usage.duration_ms ? formatDuration(usage.duration_ms) : '';
974
946
  const toolUsesText = usage.tool_uses ? `${usage.tool_uses} tool calls` : '';
975
947
  const statsText = [durationText, toolUsesText].filter(Boolean).join(' | ');
@@ -1003,7 +975,6 @@ ${createRawJsonSection(pendingTask.allEvents.slice(-3))}`;
1003
975
  await log(`🤖 Interactive mode: Task progress - ${description} (task: ${taskId}, tool: ${lastToolName})`, { verbose: true });
1004
976
  }
1005
977
  };
1006
-
1007
978
  /**
1008
979
  * Handle system.task_notification event (Agent subtask completed/failed)
1009
980
  * Updates the existing task comment with final status
@@ -1024,7 +995,6 @@ ${createRawJsonSection(pendingTask.allEvents.slice(-3))}`;
1024
995
  pendingTask.allEvents.push(data);
1025
996
 
1026
997
  let commentId = pendingTask.commentId;
1027
-
1028
998
  // Wait for comment ID if not yet available — flush queue first to avoid timeout
1029
999
  if (!commentId && pendingTask.commentIdPromise) {
1030
1000
  if (state.commentQueue.length > 0) {
@@ -1055,7 +1025,6 @@ ${createRawJsonSection(pendingTask.allEvents.slice(-3))}`;
1055
1025
  const toolUsesText = usage.tool_uses ? `${usage.tool_uses} tool calls` : '';
1056
1026
  const tokensText = usage.total_tokens ? `${usage.total_tokens.toLocaleString()} tokens` : '';
1057
1027
  const statsText = [durationText, toolUsesText, tokensText].filter(Boolean).join(' | ');
1058
-
1059
1028
  const updatedComment = `## 🤖🔀 Agent task: ${escapeMarkdown(pendingTask.description)}
1060
1029
 
1061
1030
  | Property | Value |
@@ -1070,7 +1039,6 @@ ${progressSteps ? createCollapsible(`📋 Progress steps (${pendingTask.progress
1070
1039
  ---
1071
1040
 
1072
1041
  ${createRawJsonSection([pendingTask.allEvents[0], data])}`;
1073
-
1074
1042
  await editComment(commentId, updatedComment);
1075
1043
  }
1076
1044
 
@@ -1098,7 +1066,6 @@ ${createRawJsonSection(data)}`;
1098
1066
  });
1099
1067
  }
1100
1068
  };
1101
-
1102
1069
  const { handleThinkingTokens, finalizeThinkingGroup, handleSystemStatus, handleCompactBoundary, handleTaskUpdated } = createSystemLifecycleHandlers({
1103
1070
  state,
1104
1071
  owner,
@@ -1131,7 +1098,6 @@ ${createRawJsonSection(data)}`;
1131
1098
  handleAssistantText,
1132
1099
  imageRenderer,
1133
1100
  });
1134
-
1135
1101
  /**
1136
1102
  * Handle unrecognized event types
1137
1103
  * @param {Object} data - Event data
@@ -1145,7 +1111,6 @@ ${createRawJsonSection(data)}`;
1145
1111
  This event type is not yet supported by interactive mode.
1146
1112
 
1147
1113
  ${createRawJsonSection(data)}`;
1148
-
1149
1114
  await postComment(comment);
1150
1115
 
1151
1116
  if (verbose) {
@@ -1164,7 +1129,6 @@ ${createRawJsonSection(data)}`;
1164
1129
  return;
1165
1130
  }
1166
1131
  state.eventsProcessed++;
1167
-
1168
1132
  const isThinkingTokenEvent = data.type === 'system' && data.subtype === 'thinking_tokens';
1169
1133
  if (!isThinkingTokenEvent) {
1170
1134
  await finalizeThinkingGroup();
@@ -1203,7 +1167,6 @@ ${createRawJsonSection(data)}`;
1203
1167
  case 'rate_limit_event':
1204
1168
  await handleRateLimitEvent(data);
1205
1169
  break;
1206
-
1207
1170
  case 'assistant':
1208
1171
  if (data.message && data.message.content) {
1209
1172
  const content = Array.isArray(data.message.content) ? data.message.content : [data.message.content];
@@ -1221,7 +1184,6 @@ ${createRawJsonSection(data)}`;
1221
1184
  case 'user':
1222
1185
  if (data.message && data.message.content) {
1223
1186
  const content = Array.isArray(data.message.content) ? data.message.content : [data.message.content];
1224
-
1225
1187
  for (const item of content) {
1226
1188
  if (item.type === 'tool_result') {
1227
1189
  await handleToolResult(data, item);
@@ -1241,7 +1203,6 @@ ${createRawJsonSection(data)}`;
1241
1203
  case 'turn.completed':
1242
1204
  await handleCodexTurnCompleted(data);
1243
1205
  break;
1244
-
1245
1206
  case 'error':
1246
1207
  await handleCodexError(data);
1247
1208
  break;
@@ -1273,7 +1234,6 @@ ${createRawJsonSection(data)}`;
1273
1234
  default:
1274
1235
  await handleUnrecognized(data);
1275
1236
  }
1276
-
1277
1237
  // Process any queued comments
1278
1238
  await processQueue();
1279
1239
  };
@@ -1331,7 +1291,6 @@ ${createRawJsonSection(data)}`;
1331
1291
  },
1332
1292
  };
1333
1293
  };
1334
-
1335
1294
  /**
1336
1295
  * Check if interactive mode is supported for the given tool
1337
1296
  *
@@ -1362,7 +1321,6 @@ export const validateInteractiveModeConfig = async (argv, log) => {
1362
1321
  await log(' Interactive mode will be disabled for this session.', { level: 'warning' });
1363
1322
  return false;
1364
1323
  }
1365
-
1366
1324
  await log('🔌 Interactive mode: ENABLED (experimental)', { level: 'info' });
1367
1325
  await log(` ${argv.tool || 'claude'} output will be posted as PR comments in real-time.`, { level: 'info' });
1368
1326
 
@@ -1383,7 +1341,6 @@ export const utils = {
1383
1341
  execFileAsync,
1384
1342
  CONFIG,
1385
1343
  };
1386
-
1387
1344
  // Export all functions
1388
1345
  export default {
1389
1346
  createInteractiveHandler,