@link-assistant/hive-mind 1.53.0 → 1.54.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 +16 -0
- package/package.json +1 -1
- package/src/claude.lib.mjs +11 -13
- package/src/github.lib.mjs +59 -43
- package/src/interactive-mode.lib.mjs +12 -2
- package/src/solve.auto-merge-helpers.lib.mjs +9 -6
- package/src/solve.auto-merge.lib.mjs +28 -20
- package/src/solve.config.lib.mjs +5 -0
- package/src/solve.mjs +12 -8
- package/src/solve.progress-monitoring.lib.mjs +16 -11
- package/src/solve.repo-setup.lib.mjs +8 -4
- package/src/solve.repository.lib.mjs +6 -4
- package/src/solve.results.lib.mjs +64 -3
- package/src/solve.session.lib.mjs +25 -13
- package/src/solve.watch.lib.mjs +7 -2
- package/src/tool-comments.lib.mjs +271 -0
- package/src/useless-tools.lib.mjs +187 -0
package/src/solve.mjs
CHANGED
|
@@ -54,6 +54,10 @@ const { handleAutoPrCreation } = await import('./solve.auto-pr.lib.mjs');
|
|
|
54
54
|
const { setupRepositoryAndClone, verifyDefaultBranchAndStatus } = await import('./solve.repo-setup.lib.mjs');
|
|
55
55
|
const { createOrCheckoutBranch } = await import('./solve.branch.lib.mjs');
|
|
56
56
|
const { startWorkSession, endWorkSession, SESSION_TYPES } = await import('./solve.session.lib.mjs');
|
|
57
|
+
// Issue #1625: centralized markers + tracked comment posting for solve.mjs's
|
|
58
|
+
// own usage-limit notifications (so they're excluded from the
|
|
59
|
+
// "did the AI post anything?" check in --auto-attach-solution-summary).
|
|
60
|
+
const { postTrackedComment, USAGE_LIMIT_REACHED_MARKER } = await import('./tool-comments.lib.mjs');
|
|
57
61
|
const { prepareFeedbackAndTimestamps, checkUncommittedChanges, checkForkActions } = await import('./solve.preparation.lib.mjs');
|
|
58
62
|
const { validateAndExitOnInvalidModel } = await import('./models/index.mjs');
|
|
59
63
|
const { autoAcceptInviteForRepo } = await import('./solve.accept-invite.lib.mjs');
|
|
@@ -964,11 +968,11 @@ try {
|
|
|
964
968
|
// Format the reset time with relative time and UTC conversion if available
|
|
965
969
|
const timezone = global.limitTimezone || null;
|
|
966
970
|
const formattedResetTime = resetTime ? formatResetTimeWithRelative(resetTime, timezone) : null;
|
|
967
|
-
const failureComment = formattedResetTime ? `❌
|
|
971
|
+
const failureComment = formattedResetTime ? `❌ **${USAGE_LIMIT_REACHED_MARKER}**\n\nThe AI tool has reached its usage limit. The limit will reset at: **${formattedResetTime}**\n\n${resumeSection}` : `❌ **${USAGE_LIMIT_REACHED_MARKER}**\n\nThe AI tool has reached its usage limit. Please wait for the limit to reset.\n\n${resumeSection}`;
|
|
968
972
|
|
|
969
|
-
const
|
|
970
|
-
if (
|
|
971
|
-
await log(
|
|
973
|
+
const posted = await postTrackedComment({ $, owner, repo, targetNumber: prNumber, body: failureComment });
|
|
974
|
+
if (posted.ok) {
|
|
975
|
+
await log(` Posted failure comment to PR${posted.commentId ? ` (id=${posted.commentId})` : ''}`);
|
|
972
976
|
}
|
|
973
977
|
} catch (error) {
|
|
974
978
|
await log(` Warning: Could not post failure comment: ${cleanErrorMessage(error)}`, { verbose: true });
|
|
@@ -1049,11 +1053,11 @@ try {
|
|
|
1049
1053
|
// Format reset time with relative time and UTC for better user understanding
|
|
1050
1054
|
// See: https://github.com/link-assistant/hive-mind/issues/1236
|
|
1051
1055
|
const waitingResetTimeFormatted = formatResetTimeWithRelative(global.limitResetTime, global.limitTimezone || null) || global.limitResetTime;
|
|
1052
|
-
const waitingComment = `⏳
|
|
1056
|
+
const waitingComment = `⏳ **${USAGE_LIMIT_REACHED_MARKER} - Waiting to ${limitContinueMode === 'restart' ? 'Restart' : 'Continue'}**\n\nThe AI tool has reached its usage limit. ${continueModeName} is enabled.\n\n**Reset time:** ${waitingResetTimeFormatted}\n**Wait time:** ${formatWaitTime(waitMs)} (days:hours:minutes:seconds)\n\n${continueDescription}\n\nSession ID: \`${sessionId}\``;
|
|
1053
1057
|
|
|
1054
|
-
const
|
|
1055
|
-
if (
|
|
1056
|
-
await log(
|
|
1058
|
+
const posted = await postTrackedComment({ $, owner, repo, targetNumber: prNumber, body: waitingComment });
|
|
1059
|
+
if (posted.ok) {
|
|
1060
|
+
await log(` Posted waiting comment to PR${posted.commentId ? ` (id=${posted.commentId})` : ''}`);
|
|
1057
1061
|
}
|
|
1058
1062
|
} catch (error) {
|
|
1059
1063
|
await log(` Warning: Could not post waiting comment: ${cleanErrorMessage(error)}`, { verbose: true });
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
* @experimental
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
+
// Issue #1625: centralized markers + tracking helpers so the live-progress
|
|
28
|
+
// comment is excluded from --auto-attach-solution-summary's AI-comment check.
|
|
29
|
+
import { LIVE_PROGRESS_SECTION_START_MARKER, LIVE_PROGRESS_SECTION_END_MARKER, postTrackedCommentFromFile, trackToolCommentId } from './tool-comments.lib.mjs';
|
|
30
|
+
|
|
27
31
|
/**
|
|
28
32
|
* Configuration constants for progress monitoring
|
|
29
33
|
*/
|
|
@@ -33,9 +37,10 @@ const CONFIG = {
|
|
|
33
37
|
// Progress bar characters
|
|
34
38
|
PROGRESS_CHAR_FILLED: '█',
|
|
35
39
|
PROGRESS_CHAR_EMPTY: '░',
|
|
36
|
-
// Marker comments for identifying the progress section
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
// Marker comments for identifying the progress section (imported from
|
|
41
|
+
// tool-comments.lib.mjs — single source of truth).
|
|
42
|
+
PROGRESS_SECTION_START: LIVE_PROGRESS_SECTION_START_MARKER,
|
|
43
|
+
PROGRESS_SECTION_END: LIVE_PROGRESS_SECTION_END_MARKER,
|
|
39
44
|
// Minimum interval between PR description updates (in ms)
|
|
40
45
|
MIN_UPDATE_INTERVAL: 10000, // 10 seconds to avoid rate limiting
|
|
41
46
|
// Valid display modes
|
|
@@ -223,25 +228,25 @@ export const createProgressMonitor = ({ owner, repo, prNumber, $, log, verbose =
|
|
|
223
228
|
await $`gh api repos/${owner}/${repo}/issues/comments/${state.commentId} --method PATCH --field body=@${tempFile}`;
|
|
224
229
|
await fs.unlink(tempFile).catch(() => {});
|
|
225
230
|
} else {
|
|
226
|
-
// Create new comment
|
|
231
|
+
// Create new comment. Issue #1625: post via postTrackedCommentFromFile
|
|
232
|
+
// so the comment ID is captured directly from the GitHub API response
|
|
233
|
+
// (and registered in the in-memory tracking set that excludes tool-
|
|
234
|
+
// posted comments from the "did the AI post anything?" check).
|
|
227
235
|
const fs = (await import('fs')).promises;
|
|
228
236
|
const tempFile = `/tmp/pr-progress-comment-${prNumber}-${Date.now()}.md`;
|
|
229
237
|
await fs.writeFile(tempFile, progressSection);
|
|
230
|
-
const
|
|
238
|
+
const posted = await postTrackedCommentFromFile({ $, owner, repo, targetNumber: prNumber, bodyFile: tempFile });
|
|
231
239
|
await fs.unlink(tempFile).catch(() => {});
|
|
232
240
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const output = result.stdout?.toString?.() || '';
|
|
236
|
-
const urlMatch = output.match(/\/comments\/(\d+)/);
|
|
237
|
-
if (urlMatch) {
|
|
238
|
-
state.commentId = urlMatch[1];
|
|
241
|
+
if (posted.ok && posted.commentId) {
|
|
242
|
+
state.commentId = posted.commentId;
|
|
239
243
|
} else {
|
|
240
244
|
// Fallback: find the comment we just created by looking for our marker
|
|
241
245
|
const commentsResult = await $`gh api repos/${owner}/${repo}/issues/${prNumber}/comments --jq ${`[.[] | select(.body | contains("${CONFIG.PROGRESS_SECTION_START}")) | .id] | last`}`;
|
|
242
246
|
const commentId = commentsResult.stdout?.toString?.().trim();
|
|
243
247
|
if (commentId && commentId !== 'null') {
|
|
244
248
|
state.commentId = commentId;
|
|
249
|
+
trackToolCommentId(commentId);
|
|
245
250
|
}
|
|
246
251
|
}
|
|
247
252
|
}
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* Handles repository cloning, forking, and remote setup
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
// Issue #1625: centralized markers + tracked posting for the "empty repo"
|
|
7
|
+
// issue comment so it's excluded from --auto-attach-solution-summary's check.
|
|
8
|
+
import { REPOSITORY_INITIALIZATION_REQUIRED_MARKER, postTrackedComment } from './tool-comments.lib.mjs';
|
|
9
|
+
|
|
6
10
|
export async function setupRepositoryAndClone({ argv, owner, repo, forkOwner, forkRepoName, tempDir, isContinueMode, issueUrl, log, $, needsClone = true }) {
|
|
7
11
|
// Set up repository and handle forking
|
|
8
12
|
const { repoToClone, forkedRepo, upstreamRemote, prForkOwner } = await setupRepository(argv, owner, repo, forkOwner, issueUrl, forkRepoName);
|
|
@@ -223,7 +227,7 @@ async function tryCommentOnIssueAboutEmptyRepo({ issueUrl, owner, repo, log, for
|
|
|
223
227
|
const issueNumber = issueMatch[1];
|
|
224
228
|
await log(`${formatAligned('💬', 'Creating comment:', 'Informing about empty repository...')}`);
|
|
225
229
|
|
|
226
|
-
const commentBody = `## ⚠️
|
|
230
|
+
const commentBody = `## ⚠️ ${REPOSITORY_INITIALIZATION_REQUIRED_MARKER}
|
|
227
231
|
|
|
228
232
|
Hello! I attempted to work on this issue, but encountered a problem:
|
|
229
233
|
|
|
@@ -245,9 +249,9 @@ Once the repository contains at least one commit with any file, I'll be able to
|
|
|
245
249
|
|
|
246
250
|
Thank you!`;
|
|
247
251
|
|
|
248
|
-
const
|
|
249
|
-
if (
|
|
250
|
-
await log(`${formatAligned('✅', 'Comment created:', `Posted to issue #${issueNumber}`)}`);
|
|
252
|
+
const posted = await postTrackedComment({ $, owner, repo, targetNumber: issueNumber, body: commentBody });
|
|
253
|
+
if (posted.ok) {
|
|
254
|
+
await log(`${formatAligned('✅', 'Comment created:', `Posted to issue #${issueNumber}${posted.commentId ? ` (id=${posted.commentId})` : ''}`)}`);
|
|
251
255
|
} else {
|
|
252
256
|
await log(`${formatAligned('⚠️', 'Note:', 'Could not post comment to issue (this is not critical)')}`);
|
|
253
257
|
}
|
|
@@ -32,6 +32,8 @@ import { safeExit } from './exit-handler.lib.mjs';
|
|
|
32
32
|
// Import GitHub utilities for permission checks
|
|
33
33
|
const githubLib = await import('./github.lib.mjs');
|
|
34
34
|
const { checkRepositoryWritePermission } = githubLib;
|
|
35
|
+
// Issue #1625: centralized markers + tracked posting.
|
|
36
|
+
const { REPOSITORY_INITIALIZATION_REQUIRED_MARKER, postTrackedComment } = await import('./tool-comments.lib.mjs');
|
|
35
37
|
|
|
36
38
|
// Get root repository (fork source or self), or null if inaccessible
|
|
37
39
|
export const getRootRepository = async (owner, repo) => {
|
|
@@ -698,7 +700,7 @@ export const setupRepository = async (argv, owner, repo, forkOwner = null, issue
|
|
|
698
700
|
const issueNumber = issueMatch[1];
|
|
699
701
|
await log(`${formatAligned('💬', 'Creating comment:', 'Requesting maintainer to initialize repository...')}`);
|
|
700
702
|
|
|
701
|
-
const commentBody = `## ⚠️
|
|
703
|
+
const commentBody = `## ⚠️ ${REPOSITORY_INITIALIZATION_REQUIRED_MARKER}
|
|
702
704
|
|
|
703
705
|
Hello! I attempted to work on this issue, but encountered a problem:
|
|
704
706
|
|
|
@@ -717,9 +719,9 @@ Once the repository contains at least one commit with any file, I'll be able to
|
|
|
717
719
|
|
|
718
720
|
Thank you!`;
|
|
719
721
|
|
|
720
|
-
const
|
|
721
|
-
if (
|
|
722
|
-
await log(`${formatAligned('✅', 'Comment created:', `Posted to issue #${issueNumber}`)}`);
|
|
722
|
+
const posted = await postTrackedComment({ $, owner, repo, targetNumber: issueNumber, body: commentBody });
|
|
723
|
+
if (posted.ok) {
|
|
724
|
+
await log(`${formatAligned('✅', 'Comment created:', `Posted to issue #${issueNumber}${posted.commentId ? ` (id=${posted.commentId})` : ''}`)}`);
|
|
723
725
|
} else {
|
|
724
726
|
await log(`${formatAligned('⚠️', 'Note:', 'Could not post comment to issue (this is not critical)')}`);
|
|
725
727
|
}
|
|
@@ -1041,12 +1041,22 @@ export const handleExecutionError = async (error, shouldAttachLogs, owner, repo,
|
|
|
1041
1041
|
await safeExit(1, 'Execution error');
|
|
1042
1042
|
};
|
|
1043
1043
|
|
|
1044
|
+
// Issue #1625: Markers and in-memory comment-ID tracking are centralized in
|
|
1045
|
+
// src/tool-comments.lib.mjs so that every place that *posts* a tool-generated
|
|
1046
|
+
// comment and the filter that *detects* them share the exact same constants.
|
|
1047
|
+
// Re-exported here for backwards compatibility with imports that expect them
|
|
1048
|
+
// from solve.results.lib.mjs.
|
|
1049
|
+
const toolComments = await import('./tool-comments.lib.mjs');
|
|
1050
|
+
export const { TOOL_GENERATED_COMMENT_MARKERS, isToolGeneratedComment, trackToolCommentId, isToolTrackedCommentId, getTrackedToolCommentIds, postTrackedComment } = toolComments;
|
|
1051
|
+
|
|
1044
1052
|
/**
|
|
1045
1053
|
* Check if new comments were created by the AI during the session.
|
|
1046
1054
|
* This is used by --auto-attach-solution-summary to determine if the AI
|
|
1047
1055
|
* already provided feedback.
|
|
1048
1056
|
*
|
|
1049
1057
|
* Issue #1263: Support for --attach-solution-summary and --auto-attach-solution-summary
|
|
1058
|
+
* Issue #1625: Filter out comments produced by solve.mjs itself (session start,
|
|
1059
|
+
* log upload, auto-restart, etc.) so they do not falsely count as AI-authored.
|
|
1050
1060
|
*
|
|
1051
1061
|
* @param {Date} referenceTime - The timestamp before tool execution
|
|
1052
1062
|
* @param {string} owner - Repository owner
|
|
@@ -1067,13 +1077,62 @@ export const checkForAiCreatedComments = async (referenceTime, owner, repo, prNu
|
|
|
1067
1077
|
return false;
|
|
1068
1078
|
}
|
|
1069
1079
|
|
|
1080
|
+
await log(`🔎 Checking comments by '${currentUser}' after ${referenceTime.toISOString()} (PR #${prNumber ?? 'none'}, issue #${issueNumber ?? 'none'})`, { verbose: true });
|
|
1081
|
+
|
|
1082
|
+
// Issue #1625: A comment counts as an "AI comment" only if it was posted
|
|
1083
|
+
// by the current user AFTER referenceTime AND solve.mjs did NOT post it
|
|
1084
|
+
// itself. We identify tool-posted comments in two ways, in order:
|
|
1085
|
+
// 1. Primary: comment ID is in the in-memory tracked set populated by
|
|
1086
|
+
// every solve.mjs posting site (postTrackedComment / trackToolCommentId).
|
|
1087
|
+
// This is robust to comment-body changes.
|
|
1088
|
+
// 2. Fallback: comment body matches a known TOOL_GENERATED_COMMENT_MARKERS
|
|
1089
|
+
// marker. This catches comments whose IDs weren't captured — for
|
|
1090
|
+
// example, on resumed sessions where the posting happened in an
|
|
1091
|
+
// earlier process, or legacy code paths that predate tracking.
|
|
1092
|
+
// Review-type inline comments cannot be posted by solve.mjs, so they are
|
|
1093
|
+
// treated as AI-authored by default.
|
|
1094
|
+
const filterNewAiComments = (comments, kind) => {
|
|
1095
|
+
const filtered = [];
|
|
1096
|
+
const skippedCounts = {};
|
|
1097
|
+
const skippedByIdCount = { n: 0 };
|
|
1098
|
+
for (const comment of comments) {
|
|
1099
|
+
if (!comment || !comment.user || comment.user.login !== currentUser) continue;
|
|
1100
|
+
if (!(new Date(comment.created_at) > referenceTime)) continue;
|
|
1101
|
+
|
|
1102
|
+
const isReview = kind === 'review';
|
|
1103
|
+
if (!isReview) {
|
|
1104
|
+
if (isToolTrackedCommentId(comment.id)) {
|
|
1105
|
+
skippedByIdCount.n += 1;
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
if (isToolGeneratedComment(comment.body)) {
|
|
1109
|
+
const markerMatch = TOOL_GENERATED_COMMENT_MARKERS.find(m => (comment.body || '').includes(m)) || 'unknown';
|
|
1110
|
+
skippedCounts[markerMatch] = (skippedCounts[markerMatch] || 0) + 1;
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
filtered.push(comment);
|
|
1115
|
+
}
|
|
1116
|
+
if (skippedByIdCount.n > 0) {
|
|
1117
|
+
log(` ⏭️ Skipped ${kind} tool-tracked comment IDs: ${skippedByIdCount.n}`, { verbose: true }).catch(() => {});
|
|
1118
|
+
}
|
|
1119
|
+
if (Object.keys(skippedCounts).length > 0) {
|
|
1120
|
+
const summary = Object.entries(skippedCounts)
|
|
1121
|
+
.map(([m, c]) => `${m}=${c}`)
|
|
1122
|
+
.join(', ');
|
|
1123
|
+
log(` ⏭️ Skipped ${kind} tool-generated comments (marker fallback): ${summary}`, { verbose: true }).catch(() => {});
|
|
1124
|
+
}
|
|
1125
|
+
return filtered;
|
|
1126
|
+
};
|
|
1127
|
+
|
|
1070
1128
|
// Check comments on the PR first (if we have a PR)
|
|
1071
1129
|
if (prNumber) {
|
|
1072
1130
|
// Check PR conversation comments
|
|
1073
1131
|
const prCommentsResult = await $`gh api repos/${owner}/${repo}/issues/${prNumber}/comments --paginate`;
|
|
1074
1132
|
if (prCommentsResult.code === 0) {
|
|
1075
1133
|
const prComments = JSON.parse(prCommentsResult.stdout.toString().trim() || '[]');
|
|
1076
|
-
const newPrComments = prComments
|
|
1134
|
+
const newPrComments = filterNewAiComments(prComments, 'pr');
|
|
1135
|
+
await log(` 📨 PR conversation comments after referenceTime by '${currentUser}' (excluding tool-generated): ${newPrComments.length}`, { verbose: true });
|
|
1077
1136
|
if (newPrComments.length > 0) {
|
|
1078
1137
|
return true;
|
|
1079
1138
|
}
|
|
@@ -1083,7 +1142,8 @@ export const checkForAiCreatedComments = async (referenceTime, owner, repo, prNu
|
|
|
1083
1142
|
const reviewCommentsResult = await $`gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate`;
|
|
1084
1143
|
if (reviewCommentsResult.code === 0) {
|
|
1085
1144
|
const reviewComments = JSON.parse(reviewCommentsResult.stdout.toString().trim() || '[]');
|
|
1086
|
-
const newReviewComments = reviewComments
|
|
1145
|
+
const newReviewComments = filterNewAiComments(reviewComments, 'review');
|
|
1146
|
+
await log(` 📝 PR review (inline) comments after referenceTime by '${currentUser}': ${newReviewComments.length}`, { verbose: true });
|
|
1087
1147
|
if (newReviewComments.length > 0) {
|
|
1088
1148
|
return true;
|
|
1089
1149
|
}
|
|
@@ -1095,7 +1155,8 @@ export const checkForAiCreatedComments = async (referenceTime, owner, repo, prNu
|
|
|
1095
1155
|
const issueCommentsResult = await $`gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --paginate`;
|
|
1096
1156
|
if (issueCommentsResult.code === 0) {
|
|
1097
1157
|
const issueComments = JSON.parse(issueCommentsResult.stdout.toString().trim() || '[]');
|
|
1098
|
-
const newIssueComments = issueComments
|
|
1158
|
+
const newIssueComments = filterNewAiComments(issueComments, 'issue');
|
|
1159
|
+
await log(` 📨 Issue comments after referenceTime by '${currentUser}' (excluding tool-generated): ${newIssueComments.length}`, { verbose: true });
|
|
1099
1160
|
if (newIssueComments.length > 0) {
|
|
1100
1161
|
return true;
|
|
1101
1162
|
}
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* Handles starting and ending work sessions, PR status changes, and session comments
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
// Issue #1625: Use the single source of truth for session-comment marker
|
|
7
|
+
// strings. Building comment bodies via these constants guarantees the filter
|
|
8
|
+
// in checkForAiCreatedComments() always matches what we actually posted.
|
|
9
|
+
import { AI_WORK_SESSION_STARTED_MARKER, AI_WORK_SESSION_COMPLETED_MARKER, AI_WORK_SESSION_RESUMED_MARKER, AUTO_RESUME_ON_LIMIT_RESET_MARKER, AUTO_RESTART_ON_LIMIT_RESET_MARKER, postTrackedComment } from './tool-comments.lib.mjs';
|
|
10
|
+
|
|
6
11
|
/**
|
|
7
12
|
* Session type definitions for different work session contexts
|
|
8
13
|
* See: https://github.com/link-assistant/hive-mind/issues/1152
|
|
@@ -27,26 +32,26 @@ function getSessionCommentContent(sessionType, timestamp) {
|
|
|
27
32
|
case SESSION_TYPES.RESUME:
|
|
28
33
|
return {
|
|
29
34
|
emoji: '🔄',
|
|
30
|
-
header:
|
|
35
|
+
header: AI_WORK_SESSION_RESUMED_MARKER,
|
|
31
36
|
description: `Resuming automated work session at ${isoTime}\n\nThis session continues from a previous session using the \`--resume\` flag.\n\nThe PR has been converted to draft mode while work is in progress.\n\n_This comment marks the resumption of an AI work session. Please wait for the session to finish, and provide your feedback._`,
|
|
32
37
|
};
|
|
33
38
|
case SESSION_TYPES.AUTO_RESUME:
|
|
34
39
|
return {
|
|
35
40
|
emoji: '⏰',
|
|
36
|
-
header:
|
|
41
|
+
header: AUTO_RESUME_ON_LIMIT_RESET_MARKER,
|
|
37
42
|
description: `Auto-resuming automated work session at ${isoTime}\n\nThis session automatically resumed after the usage limit reset, continuing with the previous context preserved.\n\nThe PR has been converted to draft mode while work is in progress.\n\n_This is an auto-resumed session. Please wait for the session to finish, and provide your feedback._`,
|
|
38
43
|
};
|
|
39
44
|
case SESSION_TYPES.AUTO_RESTART:
|
|
40
45
|
return {
|
|
41
46
|
emoji: '🔄',
|
|
42
|
-
header:
|
|
47
|
+
header: AUTO_RESTART_ON_LIMIT_RESET_MARKER,
|
|
43
48
|
description: `Auto-restarting automated work session at ${isoTime}\n\nThis session automatically restarted after the usage limit reset (fresh start without previous context).\n\nThe PR has been converted to draft mode while work is in progress.\n\n_This is a fresh restart after limit reset. Please wait for the session to finish, and provide your feedback._`,
|
|
44
49
|
};
|
|
45
50
|
case SESSION_TYPES.NEW:
|
|
46
51
|
default:
|
|
47
52
|
return {
|
|
48
53
|
emoji: '🤖',
|
|
49
|
-
header:
|
|
54
|
+
header: AI_WORK_SESSION_STARTED_MARKER,
|
|
50
55
|
description: `Starting automated work session at ${isoTime}\n\nThe PR has been converted to draft mode while work is in progress.\n\n_This comment marks the beginning of an AI work session. Please wait for the session to finish, and provide your feedback._`,
|
|
51
56
|
};
|
|
52
57
|
}
|
|
@@ -97,13 +102,17 @@ export async function startWorkSession({ isContinueMode, prNumber, argv, log, fo
|
|
|
97
102
|
await log('Warning: Could not check/convert PR draft status', { level: 'warning' });
|
|
98
103
|
}
|
|
99
104
|
|
|
100
|
-
// Post a comment marking the start of work session with appropriate header based on session type
|
|
105
|
+
// Post a comment marking the start of work session with appropriate header based on session type.
|
|
106
|
+
// Issue #1625: Use postTrackedComment so the comment ID is registered in-memory and can be
|
|
107
|
+
// excluded from the "did the AI post anything?" check in checkForAiCreatedComments().
|
|
101
108
|
try {
|
|
102
109
|
const { emoji, header, description } = getSessionCommentContent(sessionType, workStartTime);
|
|
103
110
|
const startComment = `${emoji} **${header}**\n\n${description}`;
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
await log(formatAligned('💬', 'Posted:', `${header} comment`, 2));
|
|
111
|
+
const { ok, commentId, stderr } = await postTrackedComment({ $, owner: global.owner, repo: global.repo, targetNumber: prNumber, body: startComment });
|
|
112
|
+
if (ok) {
|
|
113
|
+
await log(formatAligned('💬', 'Posted:', `${header} comment${commentId ? ` (id=${commentId})` : ''}`, 2));
|
|
114
|
+
} else {
|
|
115
|
+
await log(`Warning: Could not post work start comment: ${stderr || 'unknown error'}`, { level: 'warning' });
|
|
107
116
|
}
|
|
108
117
|
} catch (error) {
|
|
109
118
|
const sentryLib = await import('./sentry.lib.mjs');
|
|
@@ -129,12 +138,15 @@ export async function endWorkSession({ isContinueMode, prNumber, argv, log, form
|
|
|
129
138
|
// Only post end comment if logs were NOT already attached
|
|
130
139
|
// The attachLogToGitHub comment already serves as finishing status with "Now working session is ended" text
|
|
131
140
|
if (!logsAttached) {
|
|
132
|
-
// Post a comment marking the end of work session
|
|
141
|
+
// Post a comment marking the end of work session.
|
|
142
|
+
// Issue #1625: Track the comment ID so it won't be mistaken for AI-authored content.
|
|
133
143
|
try {
|
|
134
|
-
const endComment = `🤖
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
137
|
-
await log(formatAligned('💬', 'Posted:',
|
|
144
|
+
const endComment = `🤖 **${AI_WORK_SESSION_COMPLETED_MARKER}**\n\nWork session ended at ${workEndTime.toISOString()}\n\nThe PR will be converted back to ready for review.\n\n_This comment marks the end of an AI work session. New comments after this time will be considered as feedback._`;
|
|
145
|
+
const { ok, commentId, stderr } = await postTrackedComment({ $, owner: global.owner, repo: global.repo, targetNumber: prNumber, body: endComment });
|
|
146
|
+
if (ok) {
|
|
147
|
+
await log(formatAligned('💬', 'Posted:', `Work session end comment${commentId ? ` (id=${commentId})` : ''}`, 2));
|
|
148
|
+
} else {
|
|
149
|
+
await log(`Warning: Could not post work end comment: ${stderr || 'unknown error'}`, { level: 'warning' });
|
|
138
150
|
}
|
|
139
151
|
} catch (error) {
|
|
140
152
|
const sentryLib = await import('./sentry.lib.mjs');
|
package/src/solve.watch.lib.mjs
CHANGED
|
@@ -40,6 +40,10 @@ const { checkPRMerged, checkForUncommittedChanges, getUncommittedChangesDetails,
|
|
|
40
40
|
// Issue #1574: Interruptible sleep so CTRL+C is never blocked by a lingering timer
|
|
41
41
|
const { interruptibleSleep } = await import('./interruptible-sleep.lib.mjs');
|
|
42
42
|
|
|
43
|
+
// Issue #1625: Central marker constants + tracked comment posting
|
|
44
|
+
const toolComments = await import('./tool-comments.lib.mjs');
|
|
45
|
+
const { AUTO_RESTART_MARKER, postTrackedComment } = toolComments;
|
|
46
|
+
|
|
43
47
|
/**
|
|
44
48
|
* Monitor for feedback in a loop and trigger restart when detected
|
|
45
49
|
*/
|
|
@@ -192,8 +196,9 @@ export const watchForFeedback = async params => {
|
|
|
192
196
|
uncommittedFilesList = '\n\n**Uncommitted files:**\n```\n' + changes.join('\n') + '\n```';
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
const commentBody = `## 🔄
|
|
196
|
-
|
|
199
|
+
const commentBody = `## 🔄 ${AUTO_RESTART_MARKER} ${autoRestartCount}/${maxAutoRestartIterations}\n\nDetected uncommitted changes from previous run. Starting new session to review and commit or discard them.${uncommittedFilesList}\n\n---\n*Auto-restart will stop after changes are committed or discarded, or after ${remainingIterations} more iteration${remainingIterations !== 1 ? 's' : ''}. Please wait until working session will end and give your feedback.*`;
|
|
200
|
+
// Issue #1625: Track so this doesn't falsely count as AI-authored.
|
|
201
|
+
await postTrackedComment({ $, owner, repo, targetNumber: prNumber, body: commentBody });
|
|
197
202
|
await log(formatAligned('', '💬 Posted auto-restart notification to PR', '', 2));
|
|
198
203
|
} catch (commentError) {
|
|
199
204
|
reportError(commentError, {
|