@link-assistant/hive-mind 1.49.2 → 1.50.0
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 +25 -0
- package/package.json +1 -1
- package/src/github.lib.mjs +7 -7
- package/src/interactive-mode.lib.mjs +103 -35
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.50.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4aed1c1: fix: interactive mode GitHub comments display improvements (#1576)
|
|
8
|
+
- Fix agent task comments stuck at "⏳ Running..." by propagating taskId through comment queue
|
|
9
|
+
- Fix misleading token counts by preferring modelUsage (cumulative per-model) over usage (last-iteration)
|
|
10
|
+
- Change truncation format from "[N lines truncated]" to "[X-Y lines are omitted]" showing actual line range
|
|
11
|
+
- Rename "Session Complete" to "Interactive session completed"
|
|
12
|
+
- Rename Write tool "Content" to "Change", expand by default, add line numbers to diffs
|
|
13
|
+
- Show checked/total count in TodoWrite: "Todos (2/9 items)" instead of "Todos (9 items)"
|
|
14
|
+
- Make Task prompt and Edit Change sections expanded by default
|
|
15
|
+
- Add ToolSearch-specific display with Query/Max Results fields
|
|
16
|
+
- Mark sub-agent tasks with 🤖🔀 emoji and Agent ID field
|
|
17
|
+
- Add queue flushing before waiting for comment IDs in task progress/notification handlers
|
|
18
|
+
|
|
19
|
+
## 1.49.3
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- b15a494: fix: make usage limit footer message consistent with auto-resume mode (#1569)
|
|
24
|
+
- Fix footer message in "Usage Limit Reached" GitHub comments to reflect auto-resume/auto-restart mode
|
|
25
|
+
- Previously the footer always showed "You can resume once the limit resets." even when auto-resume was enabled
|
|
26
|
+
- Now shows mode-specific messages: "The session will automatically resume when the limit resets." or "The session will automatically restart when the limit resets."
|
|
27
|
+
|
|
3
28
|
## 1.49.2
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/package.json
CHANGED
package/src/github.lib.mjs
CHANGED
|
@@ -491,11 +491,7 @@ The automated solution draft was interrupted because the ${toolName} usage limit
|
|
|
491
491
|
const modeName = autoResumeMode === 'restart' ? 'restart' : 'resume';
|
|
492
492
|
const modeDescription = autoResumeMode === 'restart' ? 'The session will automatically restart (fresh start) when the limit resets.' : 'The session will automatically resume (with context preserved) when the limit resets.';
|
|
493
493
|
|
|
494
|
-
|
|
495
|
-
logComment += `**Auto-${modeName} is enabled.** ${modeDescription}`;
|
|
496
|
-
} else {
|
|
497
|
-
logComment += `**Auto-${modeName} is enabled.** ${modeDescription}`;
|
|
498
|
-
}
|
|
494
|
+
logComment += `**Auto-${modeName} is enabled.** ${modeDescription}`;
|
|
499
495
|
} else {
|
|
500
496
|
// Manual resume mode - show CLI commands
|
|
501
497
|
if (limitResetTime) {
|
|
@@ -516,6 +512,8 @@ ${resumeCommand}
|
|
|
516
512
|
}
|
|
517
513
|
}
|
|
518
514
|
|
|
515
|
+
const footerNote = isAutoResumeEnabled ? (autoResumeMode === 'restart' ? '*This session was interrupted due to usage limits. The session will automatically restart when the limit resets.*' : '*This session was interrupted due to usage limits. The session will automatically resume when the limit resets.*') : '*This session was interrupted due to usage limits. You can resume once the limit resets.*';
|
|
516
|
+
|
|
519
517
|
logComment += `${modelInfoString}
|
|
520
518
|
|
|
521
519
|
<details>
|
|
@@ -528,7 +526,7 @@ ${logContent}
|
|
|
528
526
|
</details>
|
|
529
527
|
|
|
530
528
|
---
|
|
531
|
-
|
|
529
|
+
${footerNote}`;
|
|
532
530
|
} else if (errorMessage) {
|
|
533
531
|
// Failure log format (non-usage-limit errors)
|
|
534
532
|
logComment = `## 🚨 Solution Draft Failed
|
|
@@ -707,13 +705,15 @@ ${resumeCommand}
|
|
|
707
705
|
}
|
|
708
706
|
}
|
|
709
707
|
|
|
708
|
+
const uploadFooterNote = isAutoResumeEnabled ? (autoResumeMode === 'restart' ? '*This session was interrupted due to usage limits. The session will automatically restart when the limit resets.*' : '*This session was interrupted due to usage limits. The session will automatically resume when the limit resets.*') : '*This session was interrupted due to usage limits. You can resume once the limit resets.*';
|
|
709
|
+
|
|
710
710
|
logUploadComment += `${modelInfoString}
|
|
711
711
|
|
|
712
712
|
### 📎 **Execution log uploaded as ${uploadTypeLabel}${chunkInfo}** (${Math.round(logStats.size / 1024)}KB)
|
|
713
713
|
- [View complete execution log](${logUrl})
|
|
714
714
|
|
|
715
715
|
---
|
|
716
|
-
|
|
716
|
+
${uploadFooterNote}`;
|
|
717
717
|
} else if (errorMessage) {
|
|
718
718
|
// Failure log format (non-usage-limit errors)
|
|
719
719
|
logUploadComment = `## 🚨 Solution Draft Failed
|
|
@@ -143,9 +143,11 @@ const truncateMiddle = (content, options = {}) => {
|
|
|
143
143
|
|
|
144
144
|
const startLines = lines.slice(0, keepStart);
|
|
145
145
|
const endLines = lines.slice(-keepEnd);
|
|
146
|
-
|
|
146
|
+
// Show the actual line number range that was omitted (1-based)
|
|
147
|
+
const omitStart = keepStart + 1;
|
|
148
|
+
const omitEnd = lines.length - keepEnd;
|
|
147
149
|
|
|
148
|
-
return sanitizeUnicode([...startLines, '', `... [${
|
|
150
|
+
return sanitizeUnicode([...startLines, '', `... [${omitStart}-${omitEnd} lines are omitted] ...`, '', ...endLines].join('\n'));
|
|
149
151
|
};
|
|
150
152
|
|
|
151
153
|
/**
|
|
@@ -278,6 +280,7 @@ const getToolIcon = toolName => {
|
|
|
278
280
|
WebFetch: '🌐',
|
|
279
281
|
WebSearch: '🔍',
|
|
280
282
|
TodoWrite: '📋',
|
|
283
|
+
ToolSearch: '🔍',
|
|
281
284
|
Task: '🎯',
|
|
282
285
|
Agent: '🤖',
|
|
283
286
|
NotebookEdit: '📓',
|
|
@@ -339,10 +342,11 @@ export const createInteractiveHandler = options => {
|
|
|
339
342
|
* Post a comment to the PR (with rate limiting)
|
|
340
343
|
* @param {string} body - Comment body
|
|
341
344
|
* @param {string} [toolId] - Optional tool ID for tracking pending tool calls
|
|
345
|
+
* @param {string} [taskId] - Optional task ID for tracking pending agent tasks
|
|
342
346
|
* @returns {Promise<string|null>} Comment ID if successful, null if queued or failed
|
|
343
347
|
* @private
|
|
344
348
|
*/
|
|
345
|
-
const postComment = async (body, toolId = null) => {
|
|
349
|
+
const postComment = async (body, toolId = null, taskId = null) => {
|
|
346
350
|
if (!prNumber || !owner || !repo) {
|
|
347
351
|
if (verbose) {
|
|
348
352
|
await log('⚠️ Interactive mode: Cannot post comment - missing PR info', { verbose: true });
|
|
@@ -354,10 +358,10 @@ export const createInteractiveHandler = options => {
|
|
|
354
358
|
const timeSinceLastComment = now - state.lastCommentTime;
|
|
355
359
|
|
|
356
360
|
if (timeSinceLastComment < CONFIG.MIN_COMMENT_INTERVAL) {
|
|
357
|
-
// Queue the comment for later with toolId for tracking
|
|
358
|
-
state.commentQueue.push({ body, toolId });
|
|
361
|
+
// Queue the comment for later with toolId/taskId for tracking
|
|
362
|
+
state.commentQueue.push({ body, toolId, taskId });
|
|
359
363
|
if (verbose) {
|
|
360
|
-
await log(`📝 Interactive mode: Comment queued (${state.commentQueue.length} in queue)${toolId ? ` [tool: ${toolId}]` : ''}`, { verbose: true });
|
|
364
|
+
await log(`📝 Interactive mode: Comment queued (${state.commentQueue.length} in queue)${toolId ? ` [tool: ${toolId}]` : ''}${taskId ? ` [task: ${taskId}]` : ''}`, { verbose: true });
|
|
361
365
|
}
|
|
362
366
|
return null;
|
|
363
367
|
}
|
|
@@ -461,8 +465,8 @@ export const createInteractiveHandler = options => {
|
|
|
461
465
|
|
|
462
466
|
const queueItem = state.commentQueue.shift();
|
|
463
467
|
if (queueItem) {
|
|
464
|
-
const { body, toolId } = queueItem;
|
|
465
|
-
// Post the comment (don't pass toolId to avoid re-queueing)
|
|
468
|
+
const { body, toolId, taskId } = queueItem;
|
|
469
|
+
// Post the comment (don't pass toolId/taskId to avoid re-queueing)
|
|
466
470
|
const commentId = await postComment(body);
|
|
467
471
|
|
|
468
472
|
// If this was a tool use comment, update the pending call with the comment ID
|
|
@@ -481,6 +485,25 @@ export const createInteractiveHandler = options => {
|
|
|
481
485
|
}
|
|
482
486
|
}
|
|
483
487
|
}
|
|
488
|
+
|
|
489
|
+
// If this was a task comment, update the pending task with the comment ID
|
|
490
|
+
// Fix: task comments previously lost their commentId when queued, causing
|
|
491
|
+
// task_notification edits to fail and leaving tasks stuck at "⏳ Running..."
|
|
492
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1576
|
|
493
|
+
if (taskId && commentId) {
|
|
494
|
+
const pendingTask = state.pendingTasks.get(taskId);
|
|
495
|
+
if (pendingTask) {
|
|
496
|
+
pendingTask.commentId = commentId;
|
|
497
|
+
if (pendingTask.resolveCommentId) {
|
|
498
|
+
pendingTask.resolveCommentId(commentId);
|
|
499
|
+
}
|
|
500
|
+
if (verbose) {
|
|
501
|
+
await log(`📋 Interactive mode: Updated pending task ${taskId} with comment ID ${commentId}`, {
|
|
502
|
+
verbose: true,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
484
507
|
}
|
|
485
508
|
}
|
|
486
509
|
|
|
@@ -613,26 +636,26 @@ ${createRawJsonSection(data)}`;
|
|
|
613
636
|
keepStart: 12,
|
|
614
637
|
keepEnd: 12,
|
|
615
638
|
});
|
|
616
|
-
// Format content as diff with + prefix for added lines
|
|
639
|
+
// Format content as diff with + prefix and line numbers for added lines
|
|
617
640
|
const diffContent = truncatedContent
|
|
618
641
|
.split('\n')
|
|
619
|
-
.map(line =>
|
|
642
|
+
.map((line, i) => `+${String(i + 1).padStart(4)} | ${line}`)
|
|
620
643
|
.join('\n');
|
|
621
|
-
inputDisplay += '\n\n' + createCollapsible('📄
|
|
644
|
+
inputDisplay += '\n\n' + createCollapsible('📄 Change', '```diff\n' + escapeMarkdown(diffContent) + '\n```', true);
|
|
622
645
|
}
|
|
623
646
|
} else if (toolName === 'Edit' && input.file_path) {
|
|
624
647
|
inputDisplay = `**File:** \`${input.file_path}\``;
|
|
625
648
|
if (input.old_string && input.new_string) {
|
|
626
649
|
const truncatedOld = truncateMiddle(input.old_string, { maxLines: 15, keepStart: 6, keepEnd: 6 });
|
|
627
650
|
const truncatedNew = truncateMiddle(input.new_string, { maxLines: 15, keepStart: 6, keepEnd: 6 });
|
|
628
|
-
// Format as unified diff with - for removed lines and + for added lines
|
|
651
|
+
// Format as unified diff with - for removed lines and + for added lines, with line numbers
|
|
629
652
|
const diffOld = truncatedOld
|
|
630
653
|
.split('\n')
|
|
631
|
-
.map(line =>
|
|
654
|
+
.map((line, i) => `-${String(i + 1).padStart(4)} | ${line}`)
|
|
632
655
|
.join('\n');
|
|
633
656
|
const diffNew = truncatedNew
|
|
634
657
|
.split('\n')
|
|
635
|
-
.map(line =>
|
|
658
|
+
.map((line, i) => `+${String(i + 1).padStart(4)} | ${line}`)
|
|
636
659
|
.join('\n');
|
|
637
660
|
inputDisplay += '\n\n' + createCollapsible('🔄 Change', '```diff\n' + escapeMarkdown(diffOld + '\n' + diffNew) + '\n```', true);
|
|
638
661
|
}
|
|
@@ -665,13 +688,17 @@ ${createRawJsonSection(data)}`;
|
|
|
665
688
|
todosPreview = [...startTodos, `- _...and ${skipped} more_`, ...endTodos].join('\n');
|
|
666
689
|
}
|
|
667
690
|
|
|
668
|
-
|
|
691
|
+
const completedCount = todos.filter(t => t.status === 'completed').length;
|
|
692
|
+
inputDisplay = createCollapsible(`📋 Todos (${completedCount}/${todos.length} items)`, todosPreview, true);
|
|
669
693
|
} else if (toolName === 'Task') {
|
|
670
694
|
inputDisplay = `**Description:** ${input.description || 'N/A'}`;
|
|
671
695
|
if (input.prompt) {
|
|
672
696
|
const truncatedPrompt = truncateMiddle(input.prompt, { maxLines: 20, keepStart: 8, keepEnd: 8 });
|
|
673
|
-
inputDisplay += '\n\n' + createCollapsible('📝 Prompt', truncatedPrompt);
|
|
697
|
+
inputDisplay += '\n\n' + createCollapsible('📝 Prompt', truncatedPrompt, true);
|
|
674
698
|
}
|
|
699
|
+
} else if (toolName === 'ToolSearch') {
|
|
700
|
+
inputDisplay = `**Query:** \`${input.query || 'N/A'}\``;
|
|
701
|
+
if (input.max_results) inputDisplay += `\n**Max Results:** ${input.max_results}`;
|
|
675
702
|
} else {
|
|
676
703
|
// Generic input display
|
|
677
704
|
const inputJson = truncateMiddle(safeJsonStringify(input, 2), {
|
|
@@ -885,7 +912,7 @@ ${createRawJsonSection(data)}`;
|
|
|
885
912
|
const handleResult = async data => {
|
|
886
913
|
const isError = data.is_error || false;
|
|
887
914
|
const statusIcon = isError ? '❌' : '✅';
|
|
888
|
-
const statusText = isError ? '
|
|
915
|
+
const statusText = isError ? 'Interactive session failed' : 'Interactive session completed';
|
|
889
916
|
|
|
890
917
|
// Format result text
|
|
891
918
|
const resultText = data.result || '_No result message_';
|
|
@@ -913,9 +940,22 @@ ${createRawJsonSection(data)}`;
|
|
|
913
940
|
statsTable += `| **Cost** | ${formatCost(data.total_cost_usd)} |\n`;
|
|
914
941
|
}
|
|
915
942
|
|
|
916
|
-
// Usage breakdown
|
|
943
|
+
// Usage breakdown — prefer modelUsage (cumulative per-model totals including sub-agents)
|
|
944
|
+
// over usage (which only contains last-iteration tokens and is misleading).
|
|
945
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1576
|
|
917
946
|
let usageSection = '';
|
|
918
|
-
if (data.
|
|
947
|
+
if (data.modelUsage && Object.keys(data.modelUsage).length > 0) {
|
|
948
|
+
usageSection = '\n### 📊 Token Usage (by model)\n\n';
|
|
949
|
+
for (const [model, mu] of Object.entries(data.modelUsage)) {
|
|
950
|
+
usageSection += `**${model}:**\n\n| Type | Count |\n|------|-------|\n`;
|
|
951
|
+
if (mu.inputTokens) usageSection += `| Input | ${mu.inputTokens.toLocaleString()} |\n`;
|
|
952
|
+
if (mu.outputTokens) usageSection += `| Output | ${mu.outputTokens.toLocaleString()} |\n`;
|
|
953
|
+
if (mu.cacheCreationInputTokens) usageSection += `| Cache Creation | ${mu.cacheCreationInputTokens.toLocaleString()} |\n`;
|
|
954
|
+
if (mu.cacheReadInputTokens) usageSection += `| Cache Read | ${mu.cacheReadInputTokens.toLocaleString()} |\n`;
|
|
955
|
+
if (typeof mu.costUSD === 'number') usageSection += `| Cost | ${formatCost(mu.costUSD)} |\n`;
|
|
956
|
+
usageSection += '\n';
|
|
957
|
+
}
|
|
958
|
+
} else if (data.usage) {
|
|
919
959
|
const u = data.usage;
|
|
920
960
|
usageSection = '\n### 📊 Token Usage\n\n| Type | Count |\n|------|-------|\n';
|
|
921
961
|
if (u.input_tokens) usageSection += `| Input | ${u.input_tokens.toLocaleString()} |\n`;
|
|
@@ -954,6 +994,7 @@ ${createRawJsonSection(data)}`;
|
|
|
954
994
|
const toolUseId = data.tool_use_id || '';
|
|
955
995
|
const description = data.description || 'Agent task';
|
|
956
996
|
const taskType = data.task_type || 'unknown';
|
|
997
|
+
const agentId = data.agent_id || taskId;
|
|
957
998
|
|
|
958
999
|
// Create a promise for the comment ID (handles queued comments)
|
|
959
1000
|
let resolveCommentId;
|
|
@@ -965,13 +1006,14 @@ ${createRawJsonSection(data)}`;
|
|
|
965
1006
|
let promptSection = '';
|
|
966
1007
|
if (data.prompt) {
|
|
967
1008
|
const truncatedPrompt = truncateMiddle(data.prompt, { maxLines: 15, keepStart: 6, keepEnd: 6 });
|
|
968
|
-
promptSection = '\n\n' + createCollapsible('📝 Task prompt', truncatedPrompt);
|
|
1009
|
+
promptSection = '\n\n' + createCollapsible('📝 Task prompt', truncatedPrompt, true);
|
|
969
1010
|
}
|
|
970
1011
|
|
|
971
|
-
const comment = `##
|
|
1012
|
+
const comment = `## 🤖🔀 Agent task: ${escapeMarkdown(description)}
|
|
972
1013
|
|
|
973
1014
|
| Property | Value |
|
|
974
1015
|
|----------|-------|
|
|
1016
|
+
| **Agent ID** | \`${agentId}\` |
|
|
975
1017
|
| **Task ID** | \`${taskId || 'unknown'}\` |
|
|
976
1018
|
| **Type** | \`${taskType}\` |
|
|
977
1019
|
| **Status** | ⏳ Running... |
|
|
@@ -988,12 +1030,13 @@ ${createRawJsonSection(data)}`;
|
|
|
988
1030
|
resolveCommentId,
|
|
989
1031
|
toolUseId,
|
|
990
1032
|
description,
|
|
1033
|
+
agentId,
|
|
991
1034
|
lastProgressDescription: description,
|
|
992
1035
|
progressCount: 0,
|
|
993
1036
|
allEvents: [data],
|
|
994
1037
|
});
|
|
995
1038
|
|
|
996
|
-
const commentId = await postComment(comment, null);
|
|
1039
|
+
const commentId = await postComment(comment, null, taskId);
|
|
997
1040
|
|
|
998
1041
|
if (commentId) {
|
|
999
1042
|
const pendingTask = state.pendingTasks.get(taskId);
|
|
@@ -1028,19 +1071,29 @@ ${createRawJsonSection(data)}`;
|
|
|
1028
1071
|
|
|
1029
1072
|
let commentId = pendingTask.commentId;
|
|
1030
1073
|
|
|
1031
|
-
// Wait for comment ID if not yet available
|
|
1074
|
+
// Wait for comment ID if not yet available — flush queue first to avoid timeout
|
|
1032
1075
|
if (!commentId && pendingTask.commentIdPromise) {
|
|
1033
|
-
|
|
1034
|
-
|
|
1076
|
+
if (state.commentQueue.length > 0) {
|
|
1077
|
+
const wasProcessing = state.isProcessing;
|
|
1078
|
+
state.isProcessing = false;
|
|
1079
|
+
await processQueue();
|
|
1080
|
+
state.isProcessing = wasProcessing;
|
|
1081
|
+
}
|
|
1082
|
+
commentId = pendingTask.commentId;
|
|
1083
|
+
if (!commentId) {
|
|
1084
|
+
const timeoutPromise = new Promise(resolve => setTimeout(() => resolve(null), 15000));
|
|
1085
|
+
commentId = await Promise.race([pendingTask.commentIdPromise, timeoutPromise]);
|
|
1086
|
+
}
|
|
1035
1087
|
}
|
|
1036
1088
|
|
|
1037
1089
|
if (commentId) {
|
|
1038
|
-
// Build progress steps list from accumulated events
|
|
1090
|
+
// Build progress steps list from accumulated events, marking with agent ID
|
|
1091
|
+
const agentTag = pendingTask.agentId ? `\`[${pendingTask.agentId}]\`` : '';
|
|
1039
1092
|
const progressSteps = pendingTask.allEvents
|
|
1040
1093
|
.filter(e => e.subtype === 'task_progress')
|
|
1041
1094
|
.map(e => {
|
|
1042
1095
|
const toolIcon = e.last_tool_name ? getToolIcon(e.last_tool_name) : '🔄';
|
|
1043
|
-
return `- ${toolIcon} ${e.description || 'Working...'}`;
|
|
1096
|
+
return `- 🔀 ${agentTag} ${toolIcon} ${e.description || 'Working...'}`;
|
|
1044
1097
|
})
|
|
1045
1098
|
.join('\n');
|
|
1046
1099
|
|
|
@@ -1048,10 +1101,11 @@ ${createRawJsonSection(data)}`;
|
|
|
1048
1101
|
const toolUsesText = usage.tool_uses ? `${usage.tool_uses} tool calls` : '';
|
|
1049
1102
|
const statsText = [durationText, toolUsesText].filter(Boolean).join(' | ');
|
|
1050
1103
|
|
|
1051
|
-
const updatedComment = `##
|
|
1104
|
+
const updatedComment = `## 🤖🔀 Agent task: ${escapeMarkdown(pendingTask.description)}
|
|
1052
1105
|
|
|
1053
1106
|
| Property | Value |
|
|
1054
1107
|
|----------|-------|
|
|
1108
|
+
| **Agent ID** | \`${pendingTask.agentId || taskId}\` |
|
|
1055
1109
|
| **Task ID** | \`${taskId}\` |
|
|
1056
1110
|
| **Status** | ⏳ Running... |
|
|
1057
1111
|
| **Progress** | ${pendingTask.progressCount} updates |
|
|
@@ -1098,19 +1152,29 @@ ${createRawJsonSection(pendingTask.allEvents.slice(-3))}`;
|
|
|
1098
1152
|
|
|
1099
1153
|
let commentId = pendingTask.commentId;
|
|
1100
1154
|
|
|
1101
|
-
// Wait for comment ID if not yet available
|
|
1155
|
+
// Wait for comment ID if not yet available — flush queue first to avoid timeout
|
|
1102
1156
|
if (!commentId && pendingTask.commentIdPromise) {
|
|
1103
|
-
|
|
1104
|
-
|
|
1157
|
+
if (state.commentQueue.length > 0) {
|
|
1158
|
+
const wasProcessing = state.isProcessing;
|
|
1159
|
+
state.isProcessing = false;
|
|
1160
|
+
await processQueue();
|
|
1161
|
+
state.isProcessing = wasProcessing;
|
|
1162
|
+
}
|
|
1163
|
+
commentId = pendingTask.commentId;
|
|
1164
|
+
if (!commentId) {
|
|
1165
|
+
const timeoutPromise = new Promise(resolve => setTimeout(() => resolve(null), 15000));
|
|
1166
|
+
commentId = await Promise.race([pendingTask.commentIdPromise, timeoutPromise]);
|
|
1167
|
+
}
|
|
1105
1168
|
}
|
|
1106
1169
|
|
|
1107
1170
|
if (commentId) {
|
|
1108
|
-
// Build final progress steps list
|
|
1171
|
+
// Build final progress steps list, marking with agent ID
|
|
1172
|
+
const agentTag = pendingTask.agentId ? `\`[${pendingTask.agentId}]\`` : '';
|
|
1109
1173
|
const progressSteps = pendingTask.allEvents
|
|
1110
1174
|
.filter(e => e.subtype === 'task_progress')
|
|
1111
1175
|
.map(e => {
|
|
1112
1176
|
const toolIcon = e.last_tool_name ? getToolIcon(e.last_tool_name) : '🔄';
|
|
1113
|
-
return `- ${toolIcon} ${e.description || 'Working...'}`;
|
|
1177
|
+
return `- 🔀 ${agentTag} ${toolIcon} ${e.description || 'Working...'}`;
|
|
1114
1178
|
})
|
|
1115
1179
|
.join('\n');
|
|
1116
1180
|
|
|
@@ -1119,10 +1183,11 @@ ${createRawJsonSection(pendingTask.allEvents.slice(-3))}`;
|
|
|
1119
1183
|
const tokensText = usage.total_tokens ? `${usage.total_tokens.toLocaleString()} tokens` : '';
|
|
1120
1184
|
const statsText = [durationText, toolUsesText, tokensText].filter(Boolean).join(' | ');
|
|
1121
1185
|
|
|
1122
|
-
const updatedComment = `##
|
|
1186
|
+
const updatedComment = `## 🤖🔀 Agent task: ${escapeMarkdown(pendingTask.description)}
|
|
1123
1187
|
|
|
1124
1188
|
| Property | Value |
|
|
1125
1189
|
|----------|-------|
|
|
1190
|
+
| **Agent ID** | \`${pendingTask.agentId || taskId}\` |
|
|
1126
1191
|
| **Task ID** | \`${taskId}\` |
|
|
1127
1192
|
| **Status** | ${statusIcon} ${statusText} |
|
|
1128
1193
|
| **Summary** | ${escapeMarkdown(summary)} |
|
|
@@ -1140,8 +1205,11 @@ ${createRawJsonSection([pendingTask.allEvents[0], data])}`;
|
|
|
1140
1205
|
state.pendingTasks.delete(taskId);
|
|
1141
1206
|
} else {
|
|
1142
1207
|
// Post as standalone if no pending task
|
|
1143
|
-
const
|
|
1208
|
+
const agentId = data.agent_id || taskId;
|
|
1209
|
+
const comment = `## 🤖🔀 Agent task ${statusIcon} ${statusText}
|
|
1144
1210
|
|
|
1211
|
+
| **Agent ID** | \`${agentId}\` |
|
|
1212
|
+
|---|---|
|
|
1145
1213
|
**Summary:** ${escapeMarkdown(summary)}
|
|
1146
1214
|
|
|
1147
1215
|
---
|