@link-assistant/hive-mind 1.56.13 ā 1.56.15
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 +12 -0
- package/package.json +2 -2
- package/src/github.lib.mjs +33 -5
- package/src/session-monitor.lib.mjs +1 -0
- package/src/solve.mjs +1 -1
- package/src/telegram-bot.mjs +6 -7
- package/src/telegram-isolation.lib.mjs +1 -1
- package/src/telegram-solve-queue.lib.mjs +7 -7
- package/src/work-session-formatting.lib.mjs +14 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.56.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cdd8010: Refine the Telegram bot work-session messages: introduce `š Starting...` and `ā³ Executing...` to distinguish launch from execution, change the completion headline to `ā
Work session finished successfully` / `ā Work session failed (exit code: N)`, show duration before session, and preserve the audit infoBlock (`Requested by`, `URL`, `š Options`, `š Locked options`) on every state ā including completion and failure paths ā so admins keep a record even when users delete their original `/solve` message.
|
|
8
|
+
|
|
9
|
+
## 1.56.14
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 77d6be2: Prevent failure-log uploads from posting broken `null` links and replace green-check failure-log terminal status with neutral attachment wording.
|
|
14
|
+
|
|
3
15
|
## 1.56.13
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-assistant/hive-mind",
|
|
3
|
-
"version": "1.56.
|
|
3
|
+
"version": "1.56.15",
|
|
4
4
|
"description": "AI-powered issue solver and hive mind for collaborative problem solving",
|
|
5
5
|
"main": "src/hive.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"hive-telegram-bot": "./src/telegram-bot.mjs"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-codex-support.mjs && node tests/test-build-cost-info-string.mjs && node tests/test-claude-code-install-method.mjs && node tests/test-claude-quiet-config.mjs && node tests/test-configure-claude-bin.mjs && node tests/test-docker-release-order.mjs && node tests/test-docker-box-migration.mjs && node tests/test-hive-screens.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-pre-pr-failure-notifier-1640.mjs && node tests/test-ready-to-merge-pagination-1645.mjs && node tests/test-require-gh-paginate-rule.mjs && node tests/test-auto-restart-limits-1664.mjs && node tests/test-log-upload-output-1678.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-telegram-bot-command-aliases.mjs && node tests/test-telegram-options-before-url.mjs && node tests/test-telegram-bot-configuration-isolation-links-notation.mjs && node tests/test-extract-isolation-from-args.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-issue-1670-screen-status-monitoring.mjs && node tests/test-issue-1680-session-monitoring.mjs && node tests/test-telegram-bot-launcher.mjs",
|
|
18
|
+
"test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-codex-support.mjs && node tests/test-build-cost-info-string.mjs && node tests/test-claude-code-install-method.mjs && node tests/test-claude-quiet-config.mjs && node tests/test-configure-claude-bin.mjs && node tests/test-docker-release-order.mjs && node tests/test-docker-box-migration.mjs && node tests/test-hive-screens.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-pre-pr-failure-notifier-1640.mjs && node tests/test-ready-to-merge-pagination-1645.mjs && node tests/test-require-gh-paginate-rule.mjs && node tests/test-auto-restart-limits-1664.mjs && node tests/test-log-upload-output-1678.mjs && node tests/test-log-upload-output-1682.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-telegram-bot-command-aliases.mjs && node tests/test-telegram-options-before-url.mjs && node tests/test-telegram-bot-configuration-isolation-links-notation.mjs && node tests/test-extract-isolation-from-args.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-issue-1670-screen-status-monitoring.mjs && node tests/test-issue-1680-session-monitoring.mjs && node tests/test-issue-1684-message-formatting.mjs && node tests/test-telegram-bot-launcher.mjs",
|
|
19
19
|
"test:queue": "node tests/solve-queue.test.mjs",
|
|
20
20
|
"test:limits-display": "node tests/limits-display.test.mjs",
|
|
21
21
|
"test:usage-limit": "node tests/test-usage-limit.mjs",
|
package/src/github.lib.mjs
CHANGED
|
@@ -299,6 +299,26 @@ Thank you! š`;
|
|
|
299
299
|
return false;
|
|
300
300
|
}
|
|
301
301
|
};
|
|
302
|
+
export const selectLogUploadUrl = ({ uploadResult, isPublicRepo }) => {
|
|
303
|
+
if (!uploadResult?.success) return null;
|
|
304
|
+
|
|
305
|
+
const chunks = Number.isFinite(uploadResult.chunks) ? uploadResult.chunks : 1;
|
|
306
|
+
const rawUrl = uploadResult.rawUrl || null;
|
|
307
|
+
const pageUrl = uploadResult.url || null;
|
|
308
|
+
const canUseRawUrl = chunks === 1 && rawUrl && (isPublicRepo || uploadResult.type !== 'repository');
|
|
309
|
+
|
|
310
|
+
return canUseRawUrl ? rawUrl : pageUrl;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const isUsableLogUrl = value => typeof value === 'string' && /^https:\/\/[^\s)]+$/u.test(value);
|
|
314
|
+
|
|
315
|
+
const getLogUploadTerminalStatus = ({ errorMessage, errorDuringExecution, isUsageLimit }) => {
|
|
316
|
+
if (errorMessage) return { emoji: 'š', label: 'Failure log' };
|
|
317
|
+
if (errorDuringExecution) return { emoji: 'š', label: 'Finished-with-errors log' };
|
|
318
|
+
if (isUsageLimit) return { emoji: 'š', label: 'Usage-limit execution log' };
|
|
319
|
+
return { emoji: 'ā
', label: 'Solution draft log' };
|
|
320
|
+
};
|
|
321
|
+
|
|
302
322
|
/** Attaches a log file to a GitHub PR or issue as a comment. Returns true if upload succeeded. */
|
|
303
323
|
export async function attachLogToGitHub(options) {
|
|
304
324
|
const fs = (await use('fs')).promises;
|
|
@@ -616,8 +636,14 @@ ${logContent}
|
|
|
616
636
|
// Requirements: 1 chunk = direct raw link, >1 chunks = repo link
|
|
617
637
|
// Private repository raw URLs can contain short-lived tokens, so keep
|
|
618
638
|
// private uploads on the stable repository/tree page URL.
|
|
619
|
-
const
|
|
620
|
-
|
|
639
|
+
const logUrl = selectLogUploadUrl({ uploadResult, isPublicRepo });
|
|
640
|
+
if (!isUsableLogUrl(logUrl)) {
|
|
641
|
+
await log(' ā gh-upload-log completed but no usable log URL was resolved');
|
|
642
|
+
await log(' ā ļø Full log upload failed; not posting a broken log link');
|
|
643
|
+
await log(` š Full log remains available locally at: ${logFile}`);
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
|
|
621
647
|
const uploadTypeLabel = uploadResult.type === 'gist' ? 'Gist' : 'Repository';
|
|
622
648
|
const chunkInfo = uploadResult.chunks > 1 ? ` (${uploadResult.chunks} chunks)` : '';
|
|
623
649
|
|
|
@@ -745,7 +771,8 @@ ${sessionNote}
|
|
|
745
771
|
const posted = await postTrackedCommentFromFile({ $, owner, repo, targetNumber, bodyFile: tempCommentFile });
|
|
746
772
|
await fs.unlink(tempCommentFile).catch(() => {});
|
|
747
773
|
if (posted.ok) {
|
|
748
|
-
|
|
774
|
+
const status = getLogUploadTerminalStatus({ errorMessage, errorDuringExecution, isUsageLimit });
|
|
775
|
+
await log(` ${status.emoji} ${status.label} uploaded to ${targetName} as ${isPublicRepo ? 'public' : 'private'} ${uploadTypeLabel}${chunkInfo}${posted.commentId ? ` (comment id=${posted.commentId})` : ''}`);
|
|
749
776
|
await log(` š Log URL: ${logUrl}`);
|
|
750
777
|
await log(` š Log size: ${Math.round(logStats.size / 1024)}KB`);
|
|
751
778
|
return true;
|
|
@@ -785,7 +812,7 @@ ${sessionNote}
|
|
|
785
812
|
*/
|
|
786
813
|
async function attachRegularComment(options, logComment) {
|
|
787
814
|
const fs = (await use('fs')).promises;
|
|
788
|
-
const { targetType, targetNumber, owner, repo, $, log, logFile } = options;
|
|
815
|
+
const { targetType, targetNumber, owner, repo, $, log, logFile, errorMessage, errorDuringExecution, isUsageLimit } = options;
|
|
789
816
|
|
|
790
817
|
const targetName = targetType === 'pr' ? 'Pull Request' : 'Issue';
|
|
791
818
|
const ghCommand = targetType === 'pr' ? 'pr' : 'issue';
|
|
@@ -801,7 +828,8 @@ async function attachRegularComment(options, logComment) {
|
|
|
801
828
|
await fs.unlink(tempFile).catch(() => {});
|
|
802
829
|
|
|
803
830
|
if (posted.ok) {
|
|
804
|
-
|
|
831
|
+
const status = getLogUploadTerminalStatus({ errorMessage, errorDuringExecution, isUsageLimit });
|
|
832
|
+
await log(` ${status.emoji} ${status.label} uploaded to ${targetName} as comment${posted.commentId ? ` (id=${posted.commentId})` : ''}`);
|
|
805
833
|
await log(` š Log size: ${Math.round(logStats.size / 1024)}KB`);
|
|
806
834
|
return true;
|
|
807
835
|
} else {
|
|
@@ -257,6 +257,7 @@ export async function monitorSessions(bot, verbose = false, options = {}) {
|
|
|
257
257
|
statusResult,
|
|
258
258
|
observedEndTime: new Date(),
|
|
259
259
|
exitCode: finalExitCode,
|
|
260
|
+
infoBlock: sessionInfo?.infoBlock || '',
|
|
260
261
|
});
|
|
261
262
|
|
|
262
263
|
// Update the original reply message if messageId is available, otherwise send new message
|
package/src/solve.mjs
CHANGED
|
@@ -1124,7 +1124,7 @@ try {
|
|
|
1124
1124
|
});
|
|
1125
1125
|
|
|
1126
1126
|
if (logUploadSuccess) {
|
|
1127
|
-
await log(`
|
|
1127
|
+
await log(` š Failure logs attached to ${logTargetLabel}`);
|
|
1128
1128
|
} else {
|
|
1129
1129
|
// Issue #1212: Always show log upload failures (not just verbose)
|
|
1130
1130
|
await log(' ā ļø Failed to upload failure logs');
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -51,7 +51,7 @@ const { isChatStopped, getChatStopInfo, getStoppedChatRejectMessage, DEFAULT_STO
|
|
|
51
51
|
const { isOldMessage: _isOldMessage, isGroupChat: _isGroupChat, isChatAuthorized: _isChatAuthorized, isForwardedOrReply: _isForwardedOrReply, extractCommandFromText, extractGitHubUrl: _extractGitHubUrl } = await import('./telegram-message-filters.lib.mjs');
|
|
52
52
|
const { launchBotWithRetry } = await import('./telegram-bot-launcher.lib.mjs');
|
|
53
53
|
const { trackSession, startSessionMonitoring, hasActiveSessionForUrlAsync } = await import('./session-monitor.lib.mjs');
|
|
54
|
-
const { formatExecutingWorkSessionMessage } = await import('./work-session-formatting.lib.mjs');
|
|
54
|
+
const { formatExecutingWorkSessionMessage, formatStartingWorkSessionMessage } = await import('./work-session-formatting.lib.mjs');
|
|
55
55
|
|
|
56
56
|
const config = yargs(hideBin(process.argv))
|
|
57
57
|
.usage('Usage: hive-telegram-bot [options]')
|
|
@@ -566,7 +566,7 @@ async function executeAndUpdateMessage(ctx, startingMessage, commandName, args,
|
|
|
566
566
|
session = iso.runner.generateSessionId();
|
|
567
567
|
VERBOSE && console.log(`[VERBOSE] Using isolation (${iso.backend}), session: ${session}`);
|
|
568
568
|
result = await iso.runner.executeWithIsolation(commandName, args, { backend: iso.backend, sessionId: session, verbose: VERBOSE });
|
|
569
|
-
if (result.success) trackSession(session, { chatId: ctx.chat.id, messageId: msgId, startTime: new Date(), url: args[0], command: commandName, isolationBackend: iso.backend, sessionId: session, tool }, VERBOSE);
|
|
569
|
+
if (result.success) trackSession(session, { chatId: ctx.chat.id, messageId: msgId, startTime: new Date(), url: args[0], command: commandName, isolationBackend: iso.backend, sessionId: session, tool, infoBlock }, VERBOSE);
|
|
570
570
|
} else {
|
|
571
571
|
result = await executeStartScreen(commandName, args);
|
|
572
572
|
const match = result.success && (result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -R\s+(\S+)/));
|
|
@@ -575,19 +575,18 @@ async function executeAndUpdateMessage(ctx, startingMessage, commandName, args,
|
|
|
575
575
|
// These sessions cannot reliably detect completion (screen stays alive via
|
|
576
576
|
// `exec bash`), so active URL checks auto-expire them after 10 min.
|
|
577
577
|
// This prevents accidental duplicate commands within the timeout window.
|
|
578
|
-
if (result.success && session !== 'unknown') trackSession(session, { chatId: ctx.chat.id, messageId: msgId, startTime: new Date(), url: args[0], command: commandName, tool }, VERBOSE);
|
|
578
|
+
if (result.success && session !== 'unknown') trackSession(session, { chatId: ctx.chat.id, messageId: msgId, startTime: new Date(), url: args[0], command: commandName, tool, infoBlock }, VERBOSE);
|
|
579
579
|
}
|
|
580
580
|
if (result.warning) return safeEdit(`ā ļø ${result.warning}`);
|
|
581
581
|
if (result.success) {
|
|
582
582
|
await safeEdit(
|
|
583
583
|
formatExecutingWorkSessionMessage({
|
|
584
|
-
commandName,
|
|
585
584
|
sessionName: session,
|
|
586
585
|
isolationBackend: iso?.backend || null,
|
|
587
586
|
infoBlock,
|
|
588
587
|
})
|
|
589
588
|
);
|
|
590
|
-
} else await safeEdit(`ā Error executing ${commandName} command:\n\n\`\`\`\n${result.error || result.output}\n
|
|
589
|
+
} else await safeEdit(`ā Error executing ${commandName} command:\n\n\`\`\`\n${result.error || result.output}\n\`\`\`\n\n${infoBlock}`);
|
|
591
590
|
}
|
|
592
591
|
|
|
593
592
|
bot.command('help', async ctx => {
|
|
@@ -1012,7 +1011,7 @@ async function handleSolveCommand(ctx) {
|
|
|
1012
1011
|
|
|
1013
1012
|
const toolQueuedCount = queueStats.queuedByTool[solveTool] || 0; // tool-specific queue count (#1551)
|
|
1014
1013
|
if (check.canStart && toolQueuedCount === 0) {
|
|
1015
|
-
const startingMessage = await safeReply(ctx,
|
|
1014
|
+
const startingMessage = await safeReply(ctx, formatStartingWorkSessionMessage({ infoBlock }), { reply_to_message_id: ctx.message.message_id });
|
|
1016
1015
|
await executeAndUpdateMessage(ctx, startingMessage, 'solve', args, infoBlock, effectiveSolveIsolation, solveTool);
|
|
1017
1016
|
} else {
|
|
1018
1017
|
const queueItem = solveQueue.enqueue({ url: normalizedUrl, args, ctx, requester, infoBlock, tool: solveTool, perCommandIsolation: effectiveSolveIsolation });
|
|
@@ -1177,7 +1176,7 @@ async function handleHiveCommand(ctx) {
|
|
|
1177
1176
|
infoBlock += `${userOptionsRaw ? '\n' : '\n\n'}š Locked options: ${escapeMarkdown(hiveOverrides.join(' '))}`;
|
|
1178
1177
|
}
|
|
1179
1178
|
|
|
1180
|
-
const startingMessage = await safeReply(ctx,
|
|
1179
|
+
const startingMessage = await safeReply(ctx, formatStartingWorkSessionMessage({ infoBlock }), { reply_to_message_id: ctx.message.message_id });
|
|
1181
1180
|
await executeAndUpdateMessage(ctx, startingMessage, 'hive', args, infoBlock, effectiveHiveIsolation, hiveTool);
|
|
1182
1181
|
}
|
|
1183
1182
|
|
|
@@ -80,7 +80,7 @@ export function createIsolationAwareQueueCallback(botIsolationBackend, botIsolat
|
|
|
80
80
|
if (iso) {
|
|
81
81
|
const sid = iso.runner.generateSessionId();
|
|
82
82
|
const r = await iso.runner.executeWithIsolation(item.command || 'solve', item.args, { backend: iso.backend, sessionId: sid, verbose });
|
|
83
|
-
if (r.success) trackSession(sid, { chatId: item.ctx?.chat?.id, messageId: item.messageInfo?.messageId, startTime: new Date(), url: item.url, command: item.command || 'solve', isolationBackend: iso.backend, sessionId: sid, tool: item.tool || 'claude' }, verbose);
|
|
83
|
+
if (r.success) trackSession(sid, { chatId: item.ctx?.chat?.id, messageId: item.messageInfo?.messageId, startTime: new Date(), url: item.url, command: item.command || 'solve', isolationBackend: iso.backend, sessionId: sid, tool: item.tool || 'claude', infoBlock: item.infoBlock }, verbose);
|
|
84
84
|
return { ...r, sessionId: sid, isolationBackend: iso.backend, output: r.output || `session: ${sid}` };
|
|
85
85
|
}
|
|
86
86
|
return fallbackCallback(item);
|
|
@@ -20,7 +20,7 @@ export { formatDuration, getRunningAgentProcesses, getRunningClaudeProcesses, ge
|
|
|
20
20
|
import { formatDuration, formatWaitingReason, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningProcesses } from './telegram-solve-queue.helpers.lib.mjs';
|
|
21
21
|
export { QUEUE_CONFIG, THRESHOLD_STRATEGIES } from './queue-config.lib.mjs';
|
|
22
22
|
import { QUEUE_CONFIG } from './queue-config.lib.mjs';
|
|
23
|
-
import { formatExecutingWorkSessionMessage } from './work-session-formatting.lib.mjs';
|
|
23
|
+
import { formatExecutingWorkSessionMessage, formatStartingWorkSessionMessage } from './work-session-formatting.lib.mjs';
|
|
24
24
|
|
|
25
25
|
export const QueueItemStatus = {
|
|
26
26
|
QUEUED: 'queued',
|
|
@@ -1058,7 +1058,7 @@ export class SolveQueue {
|
|
|
1058
1058
|
this.stats.totalStarted++;
|
|
1059
1059
|
|
|
1060
1060
|
// Update message to show Starting status
|
|
1061
|
-
await this.updateItemMessage(item,
|
|
1061
|
+
await this.updateItemMessage(item, formatStartingWorkSessionMessage({ infoBlock: item.infoBlock }));
|
|
1062
1062
|
|
|
1063
1063
|
this.log(`Starting: ${item.toString()} from ${tool} queue`);
|
|
1064
1064
|
|
|
@@ -1144,17 +1144,16 @@ export class SolveQueue {
|
|
|
1144
1144
|
if (chatId && messageId) {
|
|
1145
1145
|
try {
|
|
1146
1146
|
if (result.warning) {
|
|
1147
|
-
await item.ctx.telegram.editMessageText(chatId, messageId, undefined, `ā ļø ${result.warning}`, { parse_mode: 'Markdown' });
|
|
1147
|
+
await item.ctx.telegram.editMessageText(chatId, messageId, undefined, `ā ļø ${result.warning}\n\n${item.infoBlock}`, { parse_mode: 'Markdown' });
|
|
1148
1148
|
} else if (result.success) {
|
|
1149
1149
|
const response = formatExecutingWorkSessionMessage({
|
|
1150
|
-
commandName: item.command || 'solve',
|
|
1151
1150
|
sessionName,
|
|
1152
1151
|
isolationBackend: result.isolationBackend,
|
|
1153
1152
|
infoBlock: item.infoBlock,
|
|
1154
1153
|
});
|
|
1155
1154
|
await item.ctx.telegram.editMessageText(chatId, messageId, undefined, response, { parse_mode: 'Markdown' });
|
|
1156
1155
|
} else {
|
|
1157
|
-
const response = `ā Error executing solve command:\n\n\`\`\`\n${result.error || result.output}\n
|
|
1156
|
+
const response = `ā Error executing solve command:\n\n\`\`\`\n${result.error || result.output}\n\`\`\`\n\n${item.infoBlock}`;
|
|
1158
1157
|
await item.ctx.telegram.editMessageText(chatId, messageId, undefined, response, { parse_mode: 'Markdown' });
|
|
1159
1158
|
}
|
|
1160
1159
|
} catch (error) {
|
|
@@ -1177,7 +1176,8 @@ export class SolveQueue {
|
|
|
1177
1176
|
const { chatId, messageId } = item.messageInfo || {};
|
|
1178
1177
|
if (chatId && messageId && item.ctx) {
|
|
1179
1178
|
try {
|
|
1180
|
-
|
|
1179
|
+
const errorText = item.infoBlock ? `ā Error: ${error.message}\n\n${item.infoBlock}` : `ā Error: ${error.message}`;
|
|
1180
|
+
await item.ctx.telegram.editMessageText(chatId, messageId, undefined, errorText, { parse_mode: 'Markdown' });
|
|
1181
1181
|
} catch (editError) {
|
|
1182
1182
|
// Log the edit failure for debugging
|
|
1183
1183
|
// See: https://github.com/link-assistant/hive-mind/issues/1062
|
|
@@ -1361,7 +1361,7 @@ export function createQueueExecuteCallback(executeStartScreen, trackSessionFn) {
|
|
|
1361
1361
|
const match = result.output && (result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -R\s+(\S+)/));
|
|
1362
1362
|
const session = match ? match[1] : null;
|
|
1363
1363
|
if (session) {
|
|
1364
|
-
trackSessionFn(session, { chatId: item.ctx?.chat?.id, messageId: item.messageInfo?.messageId, startTime: new Date(), url: item.url, command: 'solve', tool: item.tool || 'claude' });
|
|
1364
|
+
trackSessionFn(session, { chatId: item.ctx?.chat?.id, messageId: item.messageInfo?.messageId, startTime: new Date(), url: item.url, command: 'solve', tool: item.tool || 'claude', infoBlock: item.infoBlock });
|
|
1365
1365
|
}
|
|
1366
1366
|
}
|
|
1367
1367
|
return result;
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
const FAILURE_STATUSES = new Set(['failed', 'cancelled', 'canceled', 'error']);
|
|
2
2
|
|
|
3
|
-
function capitalizeCommandName(commandName) {
|
|
4
|
-
const normalized = commandName || 'solve';
|
|
5
|
-
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
3
|
function parseDateValue(value) {
|
|
9
4
|
if (!value) return null;
|
|
10
5
|
const date = value instanceof Date ? value : new Date(value);
|
|
@@ -46,27 +41,32 @@ export function formatSessionDurationSeconds(seconds) {
|
|
|
46
41
|
return parts.join(' ');
|
|
47
42
|
}
|
|
48
43
|
|
|
49
|
-
export function
|
|
44
|
+
export function formatStartingWorkSessionMessage({ infoBlock = '' } = {}) {
|
|
45
|
+
const details = infoBlock ? `\n\n${infoBlock}` : '';
|
|
46
|
+
return `š Starting...${details}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function formatExecutingWorkSessionMessage({ sessionName = 'unknown', isolationBackend = null, infoBlock = '' } = {}) {
|
|
50
50
|
const isolationInfo = isolationBackend ? `\nš Isolation: \`${isolationBackend}\`` : '';
|
|
51
51
|
const details = infoBlock ? `\n\n${infoBlock}` : '';
|
|
52
|
-
return `ā³
|
|
52
|
+
return `ā³ Executing...\n\nš Session: \`${sessionName}\`${isolationInfo}${details}`;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export function formatSessionCompletionMessage({ sessionName, sessionInfo, statusResult = null, observedEndTime = new Date(), exitCode = null } = {}) {
|
|
55
|
+
export function formatSessionCompletionMessage({ sessionName, sessionInfo, statusResult = null, observedEndTime = new Date(), exitCode = null, infoBlock = '' } = {}) {
|
|
56
56
|
const finalExitCode = getSessionCompletionExitCode({ exitCode, statusResult });
|
|
57
57
|
const failed = finalExitCode !== null && finalExitCode !== 0;
|
|
58
58
|
const statusEmoji = failed ? 'ā' : 'ā
';
|
|
59
|
-
const statusText = failed ? `
|
|
60
|
-
const isolationInfo = sessionInfo?.isolationBackend ? `\nš Isolation:
|
|
59
|
+
const statusText = failed ? `Work session failed (exit code: ${finalExitCode})` : 'Work session finished successfully';
|
|
60
|
+
const isolationInfo = sessionInfo?.isolationBackend ? `\nš Isolation: \`${sessionInfo.isolationBackend}\`` : '';
|
|
61
61
|
const startTime = parseDateValue(statusResult?.startTime) || parseDateValue(sessionInfo?.startTime) || observedEndTime;
|
|
62
62
|
const endTime = parseDateValue(statusResult?.endTime) || observedEndTime;
|
|
63
63
|
const durationSeconds = Math.max(0, (endTime.getTime() - startTime.getTime()) / 1000);
|
|
64
|
+
const resolvedInfoBlock = infoBlock || sessionInfo?.infoBlock || '';
|
|
65
|
+
const details = resolvedInfoBlock ? `\n\n${resolvedInfoBlock}` : '';
|
|
64
66
|
|
|
65
|
-
let message = `${statusEmoji}
|
|
66
|
-
message += `š Session: \`${sessionName || 'unknown'}\`\n`;
|
|
67
|
+
let message = `${statusEmoji} *${statusText}*\n\n`;
|
|
67
68
|
message += `ā±ļø Duration: ${formatSessionDurationSeconds(durationSeconds)}\n`;
|
|
68
|
-
message +=
|
|
69
|
-
message += 'The work session has finished. You can now review the results.';
|
|
69
|
+
message += `š Session: \`${sessionName || 'unknown'}\`${isolationInfo}${details}`;
|
|
70
70
|
|
|
71
71
|
return message;
|
|
72
72
|
}
|