@link-assistant/hive-mind 1.56.18 → 1.57.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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.57.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 272a2d4: Add live terminal watch support for hive-telegram-bot
8
+
9
+ This feature adds `/terminal_watch` plus the experimental `--auto-start-screen-watch-message` option. The command watches the log reported by `$ --status <uuid>` and updates a separate Telegram message with a terminal-sized text snapshot.
10
+
11
+ Key features:
12
+ - Manual `/terminal_watch <uuid>` command, including reply-based usage
13
+ - Configurable terminal snapshot size with `--size`, `--width`, and `--height`
14
+ - Auto-freezes the watch message and attaches the full log when the session ends
15
+ - Public repository logs can update in chat; private/unknown visibility uses DM for manual watches
16
+ - Auto-start remains off by default and never starts for private or unknown-visibility repositories
17
+
18
+ Based on the proof-of-concept from konard/telegram-terminal-bot.
19
+
20
+ ## 1.56.19
21
+
22
+ ### Patch Changes
23
+
24
+ - 0da8eba: Add a `/log` Telegram command that lets a chat owner pull the on-disk log of a `$` isolation session (`screen`, `tmux`, `docker`). The command accepts `/log <UUID>` directly or `/log` as a reply to any session message that contains a session UUID, validates the id with `$ --status`, derives the log path from start-command's `logPath` field, and uploads the file as a reply to the user. Logs from public GitHub repositories are uploaded to the same chat; logs from private (or unknown-visibility) repositories are sent via direct message after forwarding the originating session message into the DM, so private logs never leak into public chats. Access is restricted to the chat owner (Telegram `creator` status), matching the existing `/start`, `/stop`, and `/top` policy.
25
+
3
26
  ## 1.56.18
4
27
 
5
28
  ### Patch Changes
package/README.hi.md CHANGED
@@ -431,9 +431,21 @@ Hive Mind को क्रिया में देखना चाहते
431
431
  hive-telegram-bot 2>&1 | tee -a "logs/bot-$(date +%Y%m%d-%H%M%S).log"
432
432
  ```
433
433
 
434
+ **Experimental: live terminal watch**
435
+
436
+ ```bash
437
+ hive-telegram-bot --auto-start-screen-watch-message
438
+ ```
439
+
440
+ यह opt-in flag सार्वजनिक `/solve` sessions के लिए एक अलग live terminal
441
+ message शुरू करता है। Private या unknown-visibility repositories के लिए watch
442
+ message अपने आप शुरू नहीं होता।
443
+
434
444
  ### बॉट कमांड
435
445
 
436
- सभी कमांड केवल **ग्रुप चैट में** काम करते हैं (बॉट के साथ निजी संदेशों में नहीं):
446
+ अधिकांश operational commands केवल **ग्रुप चैट में** काम करते हैं (बॉट के साथ
447
+ निजी संदेशों में नहीं)। `/terminal_watch` जैसे commands, जो जानबूझकर private
448
+ updates भेजते हैं, direct messages में भी उपयोग किए जा सकते हैं:
437
449
 
438
450
  #### `/solve` - GitHub इश्यू हल करें
439
451
 
@@ -494,6 +506,22 @@ Shows:
494
506
  - Claude usage limits (session and weekly)
495
507
  ```
496
508
 
509
+ #### `/terminal_watch` - Live Session Log
510
+
511
+ ```
512
+ /terminal_watch <uuid> [--size 120x25]
513
+
514
+ Examples:
515
+ /terminal_watch 4d934f71-4cdb-4b8c-b474-582116d12c12
516
+ /terminal_watch 4d934f71-4cdb-4b8c-b474-582116d12c12 --width 100 --height 20
517
+ ```
518
+
519
+ आप bot session message पर `/terminal_watch` के साथ reply भी कर सकते हैं। यह
520
+ command `$ --status <uuid>` द्वारा report किए गए session log की latest lines के
521
+ साथ एक अलग Telegram message update करता है और session खत्म होने पर पूरी log file
522
+ attach करता है। Public repository logs chat में watch किए जा सकते हैं; private या
523
+ unknown-visibility repository logs केवल direct message से भेजे जाते हैं।
524
+
497
525
  #### `/help` - सहायता और डायग्नोस्टिक जानकारी प्राप्त करें
498
526
 
499
527
  ```
@@ -508,12 +536,25 @@ Shows:
508
536
 
509
537
  ### विशेषताएँ
510
538
 
511
- - ✅ **केवल ग्रुप चैट**: कमांड केवल ग्रुप चैट में काम करते हैं (निजी संदेश नहीं)
539
+ - ✅ **Group Chat Execution**: `/solve` और `/hive` workflows authorized group chats से चलते हैं
512
540
  - ✅ **पूर्ण विकल्प सपोर्ट**: सभी कमांड-लाइन विकल्प Telegram में काम करते हैं
513
541
  - ✅ **Screen सत्र**: कमांड डिटैच्ड screen सत्रों में चलते हैं
542
+ - ✅ **Live Terminal Watch**: `/terminal_watch` और opt-in auto-start live session logs दिखाते हैं
514
543
  - ✅ **चैट प्रतिबंध**: अनुमत चैट ID की वैकल्पिक सफेद सूची
515
544
  - ✅ **डायग्नोस्टिक टूल**: चैट ID और कॉन्फ़िगरेशन जानकारी प्राप्त करें
516
545
 
546
+ #### Live Terminal Watch
547
+
548
+ `--auto-start-screen-watch-message` से enabled होने पर, bot public `/solve`
549
+ sessions के लिए अपने आप एक अलग live terminal watch message शुरू करता है:
550
+
551
+ - **Manual Watch**: `/terminal_watch <uuid>` या `/terminal_watch` के साथ reply
552
+ - **Real-time Updates**: Commands execute होते समय live session log output देखें
553
+ - **Auto-freeze**: Command पूरा होने पर message freeze हो जाता है
554
+ - **Log Attachment**: Session खत्म होने पर full logs अपने आप attach होते हैं
555
+ - **Security**: Private या unknown-visibility repositories के लिए auto-start disabled है
556
+ - **Smart Updates**: केवल वास्तविक बदलाव मिलने पर update करता है (API limits से बचने के लिए rate-limited)
557
+
517
558
  ### सुरक्षा नोट
518
559
 
519
560
  - केवल उन ग्रुप चैट में काम करता है जहाँ बॉट एडमिन है
package/README.md CHANGED
@@ -440,9 +440,19 @@ Want to see the Hive Mind in action? Request a free demo or get faster support b
440
440
  hive-telegram-bot 2>&1 | tee -a "logs/bot-$(date +%Y%m%d-%H%M%S).log"
441
441
  ```
442
442
 
443
+ **Experimental: live terminal watch**
444
+
445
+ ```bash
446
+ hive-telegram-bot --auto-start-screen-watch-message
447
+ ```
448
+
449
+ This opt-in flag starts a separate live terminal message for public `/solve`
450
+ sessions. Private or unknown-visibility repositories never auto-start a
451
+ watch message.
452
+
443
453
  ### Bot Commands
444
454
 
445
- All commands work in **group chats only** (not in private messages with the bot):
455
+ Most operational commands work in **group chats only** (not in private messages with the bot). Commands that intentionally deliver private updates, such as `/terminal_watch`, may also be used in direct messages:
446
456
 
447
457
  #### `/solve` - Solve GitHub Issues
448
458
 
@@ -514,6 +524,22 @@ Shows:
514
524
  - Claude usage limits (session and weekly)
515
525
  ```
516
526
 
527
+ #### `/terminal_watch` - Live Session Log
528
+
529
+ ```
530
+ /terminal_watch <uuid> [--size 120x25]
531
+
532
+ Examples:
533
+ /terminal_watch 4d934f71-4cdb-4b8c-b474-582116d12c12
534
+ /terminal_watch 4d934f71-4cdb-4b8c-b474-582116d12c12 --width 100 --height 20
535
+ ```
536
+
537
+ You can also reply to a bot session message with `/terminal_watch`. The command
538
+ updates a separate Telegram message with the latest lines from the session log
539
+ reported by `$ --status <uuid>` and attaches the full log file when the session
540
+ finishes. Public repository logs can be watched in the chat; private or
541
+ unknown-visibility repository logs are delivered by direct message only.
542
+
517
543
  #### `/help` - Get Help and Diagnostic Info
518
544
 
519
545
  ```
@@ -528,12 +554,24 @@ Shows:
528
554
 
529
555
  ### Features
530
556
 
531
- - ✅ **Group Chat Only**: Commands work only in group chats (not private messages)
557
+ - ✅ **Group Chat Execution**: `/solve` and `/hive` workflows run from authorized group chats
532
558
  - ✅ **Full Options Support**: All command-line options work in Telegram
533
559
  - ✅ **Screen Sessions**: Commands run in detached screen sessions
560
+ - ✅ **Live Terminal Watch**: `/terminal_watch` and opt-in auto-start show live session logs
534
561
  - ✅ **Chat Restrictions**: Optional whitelist of allowed chat IDs
535
562
  - ✅ **Diagnostic Tools**: Get chat ID and configuration info
536
563
 
564
+ #### Live Terminal Watch
565
+
566
+ When enabled with `--auto-start-screen-watch-message`, the bot automatically starts a separate live terminal watch message for public `/solve` sessions:
567
+
568
+ - **Manual Watch**: `/terminal_watch <uuid>` or reply with `/terminal_watch`
569
+ - **Real-time Updates**: See live session log output as commands execute
570
+ - **Auto-freeze**: Message freezes when command completes
571
+ - **Log Attachment**: Full logs attached automatically when session ends
572
+ - **Security**: Auto-start is disabled for private or unknown-visibility repositories
573
+ - **Smart Updates**: Only updates when actual changes detected (rate-limited to avoid API limits)
574
+
537
575
  ### Security Notes
538
576
 
539
577
  - Only works in group chats where the bot is admin
package/README.ru.md CHANGED
@@ -431,9 +431,22 @@ Hive Mind включает интерфейс Telegram-бота (SwarmMindBot)
431
431
  hive-telegram-bot 2>&1 | tee -a "logs/bot-$(date +%Y%m%d-%H%M%S).log"
432
432
  ```
433
433
 
434
+ **Экспериментально: live terminal watch**
435
+
436
+ ```bash
437
+ hive-telegram-bot --auto-start-screen-watch-message
438
+ ```
439
+
440
+ Этот opt-in флаг запускает отдельное live terminal сообщение для публичных
441
+ сессий `/solve`. Для приватных репозиториев или репозиториев с неизвестной
442
+ видимостью watch-сообщение автоматически не запускается.
443
+
434
444
  ### Команды бота
435
445
 
436
- Все команды работают **только в групповых чатах** (не в личных сообщениях боту):
446
+ Большинство операционных команд работают **только в групповых чатах** (не в
447
+ личных сообщениях боту). Команды, которые намеренно доставляют приватные
448
+ обновления, например `/terminal_watch`, также можно использовать в личных
449
+ сообщениях:
437
450
 
438
451
  #### `/solve` — Решение задач GitHub
439
452
 
@@ -494,6 +507,23 @@ Shows:
494
507
  - Claude usage limits (session and weekly)
495
508
  ```
496
509
 
510
+ #### `/terminal_watch` — Live Session Log
511
+
512
+ ```
513
+ /terminal_watch <uuid> [--size 120x25]
514
+
515
+ Examples:
516
+ /terminal_watch 4d934f71-4cdb-4b8c-b474-582116d12c12
517
+ /terminal_watch 4d934f71-4cdb-4b8c-b474-582116d12c12 --width 100 --height 20
518
+ ```
519
+
520
+ Также можно ответить на сообщение сессии бота командой `/terminal_watch`. Команда
521
+ обновляет отдельное сообщение Telegram последними строками лога сессии,
522
+ полученного через `$ --status <uuid>`, и прикрепляет полный файл лога после
523
+ завершения сессии. Логи публичных репозиториев можно смотреть в чате; логи
524
+ приватных репозиториев или репозиториев с неизвестной видимостью доставляются
525
+ только личным сообщением.
526
+
497
527
  #### `/help` — Получить справку и диагностическую информацию
498
528
 
499
529
  ```
@@ -508,12 +538,25 @@ Shows:
508
538
 
509
539
  ### Возможности
510
540
 
511
- - ✅ **Только групповые чаты**: команды работают исключительно в групповых чатах (не в личных сообщениях)
541
+ - ✅ **Запуск из групповых чатов**: workflows `/solve` и `/hive` запускаются из авторизованных групповых чатов
512
542
  - ✅ **Полная поддержка параметров**: все параметры командной строки работают в Telegram
513
543
  - ✅ **Screen-сессии**: команды запускаются в отсоединённых screen-сессиях
544
+ - ✅ **Live Terminal Watch**: `/terminal_watch` и opt-in auto-start показывают live session logs
514
545
  - ✅ **Ограничения по чатам**: опциональный белый список разрешённых ID чатов
515
546
  - ✅ **Диагностические инструменты**: получение ID чата и информации о конфигурации
516
547
 
548
+ #### Live Terminal Watch
549
+
550
+ Если включить `--auto-start-screen-watch-message`, бот автоматически запускает
551
+ отдельное live terminal watch сообщение для публичных сессий `/solve`:
552
+
553
+ - **Manual Watch**: `/terminal_watch <uuid>` или ответ командой `/terminal_watch`
554
+ - **Real-time Updates**: смотрите live session log output во время выполнения команд
555
+ - **Auto-freeze**: сообщение замораживается после завершения команды
556
+ - **Log Attachment**: полные логи автоматически прикрепляются после завершения сессии
557
+ - **Security**: auto-start отключён для приватных репозиториев и репозиториев с неизвестной видимостью
558
+ - **Smart Updates**: обновляет сообщение только при реальных изменениях (rate-limited для защиты от API limits)
559
+
517
560
  ### Замечания по безопасности
518
561
 
519
562
  - Работает только в групповых чатах, где бот является администратором
package/README.zh.md CHANGED
@@ -431,9 +431,18 @@ Hive Mind 内置 Telegram 机器人接口(SwarmMindBot),支持远程命令
431
431
  hive-telegram-bot 2>&1 | tee -a "logs/bot-$(date +%Y%m%d-%H%M%S).log"
432
432
  ```
433
433
 
434
+ **实验性:live terminal watch**
435
+
436
+ ```bash
437
+ hive-telegram-bot --auto-start-screen-watch-message
438
+ ```
439
+
440
+ 这个 opt-in 标志会为公开仓库的 `/solve` 会话启动一条单独的 live terminal
441
+ 消息。私有仓库或可见性未知的仓库不会自动启动 watch 消息。
442
+
434
443
  ### 机器人命令
435
444
 
436
- 所有命令仅在**群聊中**有效(不支持与机器人的私聊):
445
+ 大多数操作类命令仅在**群聊中**有效(不支持与机器人的私聊)。有意发送私密更新的命令(例如 `/terminal_watch`)也可以在私聊中使用:
437
446
 
438
447
  #### `/solve` - 解决 GitHub Issue
439
448
 
@@ -494,6 +503,21 @@ Shows:
494
503
  - Claude usage limits (session and weekly)
495
504
  ```
496
505
 
506
+ #### `/terminal_watch` - Live Session Log
507
+
508
+ ```
509
+ /terminal_watch <uuid> [--size 120x25]
510
+
511
+ Examples:
512
+ /terminal_watch 4d934f71-4cdb-4b8c-b474-582116d12c12
513
+ /terminal_watch 4d934f71-4cdb-4b8c-b474-582116d12c12 --width 100 --height 20
514
+ ```
515
+
516
+ 您也可以回复机器人会话消息并发送 `/terminal_watch`。该命令会用
517
+ `$ --status <uuid>` 报告的会话日志最新内容更新一条单独的 Telegram
518
+ 消息,并在会话结束时附加完整日志文件。公开仓库日志可以在群聊中 watch;
519
+ 私有仓库或可见性未知仓库的日志只会通过私聊发送。
520
+
497
521
  #### `/help` - 获取帮助和诊断信息
498
522
 
499
523
  ```
@@ -508,12 +532,25 @@ Shows:
508
532
 
509
533
  ### 功能特性
510
534
 
511
- - ✅ **仅限群聊**:命令仅在群聊中有效(不支持私聊)
535
+ - ✅ **群聊执行**:`/solve` 和 `/hive` workflow 从已授权群聊中运行
512
536
  - ✅ **完整选项支持**:所有命令行选项均可在 Telegram 中使用
513
537
  - ✅ **Screen 会话**:命令在后台 Screen 会话中运行
538
+ - ✅ **Live Terminal Watch**:`/terminal_watch` 和 opt-in auto-start 显示 live session logs
514
539
  - ✅ **聊天限制**:可选配置允许的聊天 ID 白名单
515
540
  - ✅ **诊断工具**:获取聊天 ID 和配置信息
516
541
 
542
+ #### Live Terminal Watch
543
+
544
+ 启用 `--auto-start-screen-watch-message` 后,机器人会为公开仓库的 `/solve`
545
+ 会话自动启动一条单独的 live terminal watch 消息:
546
+
547
+ - **Manual Watch**:`/terminal_watch <uuid>` 或回复 `/terminal_watch`
548
+ - **Real-time Updates**:在命令执行时查看 live session log output
549
+ - **Auto-freeze**:命令完成时消息会被冻结
550
+ - **Log Attachment**:会话结束时自动附加完整日志
551
+ - **Security**:私有仓库或可见性未知的仓库禁用 auto-start
552
+ - **Smart Updates**:仅在实际内容变化时更新(rate-limited 以避免 API limits)
553
+
517
554
  ### 安全注意事项
518
555
 
519
556
  - 仅在机器人为管理员的群聊中有效
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.56.18",
3
+ "version": "1.57.0",
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-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-issue-1688-subscribe-and-pr-link.mjs && node tests/test-issue-1694-stabilized-defaults.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-467-terminal-watch.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-issue-1686-log-command.mjs && node tests/test-issue-1688-subscribe-and-pr-link.mjs && node tests/test-issue-1694-stabilized-defaults.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",
@@ -41,17 +41,18 @@ export function generateSessionId() {
41
41
  * Keep the parser tolerant so completion monitoring survives either format.
42
42
  *
43
43
  * @param {string} output - Raw stdout from `$ --status`
44
- * @returns {{exists: boolean, uuid: string|null, status: string|null, exitCode: number|null, startTime: string|null, endTime: string|null, currentTime: string|null, raw: string}}
44
+ * @returns {{exists: boolean, uuid: string|null, status: string|null, exitCode: number|null, startTime: string|null, endTime: string|null, currentTime: string|null, logPath: string|null, command: string|null, isolation: string|null, workingDirectory: string|null, raw: string}}
45
45
  */
46
46
  export function parseSessionStatusOutput(output) {
47
47
  const raw = (output || '').trim();
48
48
  if (!raw) {
49
- return { exists: false, uuid: null, status: null, exitCode: null, startTime: null, endTime: null, currentTime: null, raw: '' };
49
+ return { exists: false, uuid: null, status: null, exitCode: null, startTime: null, endTime: null, currentTime: null, logPath: null, command: null, isolation: null, workingDirectory: null, raw: '' };
50
50
  }
51
51
 
52
52
  try {
53
53
  const parsed = JSON.parse(raw);
54
54
  const data = Array.isArray(parsed) ? parsed[0] : parsed;
55
+ const isolationFromOptions = typeof data?.options?.isolation === 'string' ? data.options.isolation.toLowerCase() : null;
55
56
  return {
56
57
  exists: true,
57
58
  uuid: data?.uuid || null,
@@ -60,6 +61,10 @@ export function parseSessionStatusOutput(output) {
60
61
  startTime: data?.startTime || null,
61
62
  endTime: data?.endTime || null,
62
63
  currentTime: data?.currentTime || null,
64
+ logPath: data?.logPath || null,
65
+ command: data?.command || null,
66
+ isolation: typeof data?.isolation === 'string' ? data.isolation.toLowerCase() : isolationFromOptions,
67
+ workingDirectory: data?.workingDirectory || null,
63
68
  raw,
64
69
  };
65
70
  } catch {
@@ -87,6 +92,10 @@ export function parseSessionStatusOutput(output) {
87
92
  startTime: readField('startTime'),
88
93
  endTime: readField('endTime'),
89
94
  currentTime: readField('currentTime'),
95
+ logPath: readField('logPath'),
96
+ command: readField('command'),
97
+ isolation: readField('isolation')?.toLowerCase() || null,
98
+ workingDirectory: readField('workingDirectory'),
90
99
  raw,
91
100
  };
92
101
  }
@@ -213,7 +222,7 @@ export async function querySessionStatus(sessionId, verbose = false) {
213
222
  if (verbose) {
214
223
  console.log('[VERBOSE] isolation-runner: Cannot query status - $ binary not found');
215
224
  }
216
- return { exists: false, uuid: null, status: null, exitCode: null, startTime: null, endTime: null, currentTime: null, raw: '' };
225
+ return { exists: false, uuid: null, status: null, exitCode: null, startTime: null, endTime: null, currentTime: null, logPath: null, command: null, isolation: null, workingDirectory: null, raw: '' };
217
226
  }
218
227
 
219
228
  try {
@@ -230,7 +239,7 @@ export async function querySessionStatus(sessionId, verbose = false) {
230
239
  if (verbose) {
231
240
  console.log(`[VERBOSE] isolation-runner: Status query error: ${error.message}`);
232
241
  }
233
- return { exists: false, uuid: null, status: null, exitCode: null, startTime: null, endTime: null, currentTime: null, raw: '' };
242
+ return { exists: false, uuid: null, status: null, exitCode: null, startTime: null, endTime: null, currentTime: null, logPath: null, command: null, isolation: null, workingDirectory: null, raw: '' };
234
243
  }
235
244
  }
236
245
 
@@ -89,6 +89,21 @@ export function trackSession(sessionName, sessionInfo, verbose = false) {
89
89
  }
90
90
  }
91
91
 
92
+ /**
93
+ * Look up the in-memory record for a session id (UUID for isolation sessions
94
+ * or the screen session name for non-isolation sessions). Returns null when no
95
+ * record exists — for example, after a process restart or for sessions that
96
+ * were never tracked through the Telegram bot. Used by `/log` to discover the
97
+ * originating chat id and the GitHub URL associated with a session.
98
+ *
99
+ * @param {string} sessionName
100
+ * @returns {Object|null}
101
+ */
102
+ export function getTrackedSessionInfo(sessionName) {
103
+ if (!sessionName) return null;
104
+ return activeSessions.get(sessionName) || null;
105
+ }
106
+
92
107
  /**
93
108
  * Get the number of active sessions being tracked
94
109
  * @param {boolean} verbose - Whether to log verbose output
@@ -49,6 +49,8 @@ const { getSolveQueue, createQueueExecuteCallback } = await import('./telegram-s
49
49
  const { applySolveToolAlias, getFirstParsedPositionalArg, getSolveCommandNameFromText, getSolveToolAliasFromText, moveArgumentToFront, parseArgsWithYargs, parseCommandArgs, SOLVE_COMMAND_NAMES } = await import('./telegram-solve-command.lib.mjs');
50
50
  const { isChatStopped, getChatStopInfo, getStoppedChatRejectMessage, DEFAULT_STOP_REASON } = await import('./telegram-start-stop-command.lib.mjs');
51
51
  const { isOldMessage: _isOldMessage, isGroupChat: _isGroupChat, isChatAuthorized: _isChatAuthorized, isForwardedOrReply: _isForwardedOrReply, extractCommandFromText, extractGitHubUrl: _extractGitHubUrl } = await import('./telegram-message-filters.lib.mjs');
52
+ const { safeReply } = await import('./telegram-safe-reply.lib.mjs');
53
+ const { registerTerminalWatchCommand, startAutoTerminalWatchForSession } = await import('./telegram-terminal-watch-command.lib.mjs');
52
54
  const { launchBotWithRetry } = await import('./telegram-bot-launcher.lib.mjs');
53
55
  const { trackSession, startSessionMonitoring, hasActiveSessionForUrlAsync } = await import('./session-monitor.lib.mjs');
54
56
  const { formatExecutingWorkSessionMessage, formatStartingWorkSessionMessage } = await import('./work-session-formatting.lib.mjs');
@@ -113,6 +115,7 @@ const config = yargs(hideBin(process.argv))
113
115
  alias: 'v',
114
116
  default: getenv('TELEGRAM_BOT_VERBOSE', 'false') === 'true',
115
117
  })
118
+ .option('autoStartScreenWatchMessage', { type: 'boolean', description: 'Experimental: auto-start separate /terminal_watch messages for public /solve sessions', alias: 'auto-start-screen-watch-message', default: getenv('TELEGRAM_AUTO_START_SCREEN_WATCH_MESSAGE', getenv('TELEGRAM_AUTO_WATCH_MESSAGE', 'false')) === 'true' })
116
119
  .option('isolation', { type: 'string', description: "Isolation backend (screen/tmux/docker). Defaults to 'screen' so Telegram-bot work sessions survive bot restarts; pass --isolation '' (or set TELEGRAM_ISOLATION='') to disable.", default: getenv('TELEGRAM_ISOLATION', 'screen') })
117
120
  .help('h')
118
121
  .alias('h', 'help')
@@ -130,6 +133,7 @@ if (config.configuration) {
130
133
 
131
134
  const BOT_TOKEN = config.token || getenv('TELEGRAM_BOT_TOKEN', '');
132
135
  const VERBOSE = config.verbose || getenv('TELEGRAM_BOT_VERBOSE', 'false') === 'true';
136
+ const AUTO_WATCH_MESSAGE = config.autoStartScreenWatchMessage === true;
133
137
  if (!BOT_TOKEN) {
134
138
  console.error('Error: TELEGRAM_BOT_TOKEN not set. Use --token or TELEGRAM_BOT_TOKEN env var.');
135
139
  process.exit(1);
@@ -532,25 +536,6 @@ async function validateGitHubUrl(args, options = {}) {
532
536
  return { valid: true, parsed, normalizedUrl: url };
533
537
  }
534
538
 
535
- // Issue #1460/#1497: safeReply - try Markdown first, fall back to plain text on parsing errors
536
- async function safeReply(ctx, text, options = {}) {
537
- try {
538
- return await ctx.reply(text, { parse_mode: 'Markdown', ...options });
539
- } catch (error) {
540
- const isParsingError = error.message && (error.message.includes("can't parse entities") || error.message.includes("Can't parse entities") || error.message.includes("can't find end of") || (error.message.includes('Bad Request') && error.message.includes('400')));
541
- if (!isParsingError) throw error;
542
- console.error(`[telegram-bot] safeReply: Markdown parsing failed: ${error.message}`);
543
- console.error(`[telegram-bot] safeReply: Failing message (${Buffer.byteLength(text, 'utf-8')} bytes): ${text}`);
544
- const plainText = text
545
- .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1 ($2)')
546
- .replace(/\\_/g, '_')
547
- .replace(/\\\*/g, '*')
548
- .replace(/\*([^*]+)\*/g, '$1')
549
- .replace(/`([^`]+)`/g, '$1');
550
- return await ctx.reply(plainText, { ...options, parse_mode: undefined });
551
- }
552
- }
553
-
554
539
  async function executeAndUpdateMessage(ctx, startingMessage, commandName, args, infoBlock, perCommandIsolation = null, tool = 'claude', urlContext = null) {
555
540
  const { chat, message_id: msgId } = startingMessage;
556
541
  const safeEdit = async text => {
@@ -562,18 +547,24 @@ async function executeAndUpdateMessage(ctx, startingMessage, commandName, args,
562
547
  };
563
548
  const requesterUserId = ctx.from?.id ?? null; // Issue #1688: suppress duplicate /subscribe DM
564
549
  const iso = await resolveIsolation(perCommandIsolation, ISOLATION_BACKEND, isolationRunner, VERBOSE);
565
- let result, session;
550
+ let result, session, sessionInfo;
566
551
  if (iso) {
567
552
  session = iso.runner.generateSessionId();
568
553
  VERBOSE && console.log(`[VERBOSE] Using isolation (${iso.backend}), session: ${session}`);
569
554
  result = await iso.runner.executeWithIsolation(commandName, args, { backend: iso.backend, sessionId: session, verbose: VERBOSE });
570
- 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, urlContext, requesterUserId }, VERBOSE);
555
+ if (result.success) {
556
+ sessionInfo = { chatId: ctx.chat.id, messageId: msgId, startTime: new Date(), url: args[0], command: commandName, isolationBackend: iso.backend, sessionId: session, tool, infoBlock, urlContext, requesterUserId };
557
+ trackSession(session, sessionInfo, VERBOSE);
558
+ }
571
559
  } else {
572
560
  result = await executeStartScreen(commandName, args);
573
561
  const match = result.success && (result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -R\s+(\S+)/));
574
562
  session = match ? match[1] : 'unknown';
575
563
  // Issue #1586: Non-isolation sessions auto-expire after 10 min — screen stays alive via `exec bash` so completion can't be detected reliably; this still blocks duplicate commands in the timeout window.
576
- if (result.success && session !== 'unknown') trackSession(session, { chatId: ctx.chat.id, messageId: msgId, startTime: new Date(), url: args[0], command: commandName, tool, infoBlock, urlContext, requesterUserId }, VERBOSE);
564
+ if (result.success && session !== 'unknown') {
565
+ sessionInfo = { chatId: ctx.chat.id, messageId: msgId, startTime: new Date(), url: args[0], command: commandName, tool, infoBlock, urlContext, requesterUserId };
566
+ trackSession(session, sessionInfo, VERBOSE);
567
+ }
577
568
  }
578
569
  if (result.warning) return safeEdit(`⚠️ ${result.warning}`);
579
570
  if (result.success) {
@@ -584,6 +575,7 @@ async function executeAndUpdateMessage(ctx, startingMessage, commandName, args,
584
575
  infoBlock,
585
576
  })
586
577
  );
578
+ if (AUTO_WATCH_MESSAGE && commandName === 'solve' && sessionInfo?.isolationBackend) await startAutoTerminalWatchForSession({ bot, ctx, sessionId: session, sessionInfo, verbose: VERBOSE });
587
579
  } else await safeEdit(`❌ Error executing ${commandName} command:\n\n\`\`\`\n${result.error || result.output}\n\`\`\`\n\n${infoBlock}`);
588
580
  }
589
581
 
@@ -662,12 +654,13 @@ bot.command('help', async ctx => {
662
654
  message += "Merges all PRs with 'ready' label sequentially.\n";
663
655
  message += '*/subscribe* / */unsubscribe* - 🔔 Get private DM forward of /solve completion (experimental, #1688)\n';
664
656
  message += '*/help* - Show this help message\n';
665
- message += '*/stop* - Stop accepting new tasks (owner only)\n';
666
- message += '*/start* - Resume accepting tasks (owner only)\n\n';
667
- message += '🔔 *Session Notifications:* The bot monitors sessions and notifies when they complete. Use /subscribe to also get DM forwards (in-memory, resets on restart).\n';
657
+ message += '*/stop* / */start* - Stop or resume accepting new tasks (owner only)\n';
658
+ message += '*/log* - Fetch isolation session log (owner only). Usage: `/log <uuid>` or reply with `/log`\n';
659
+ message += '*/terminal\\_watch* - Live-update an isolation session log (owner only). Usage: `/terminal_watch <uuid>` or reply with `/terminal_watch`\n\n';
660
+ message += '🔔 *Session Notifications:* Completion notifications are automatic; use /subscribe for private DM forwards.\n';
668
661
  if (ISOLATION_BACKEND) message += `🔒 *Isolation Mode:* \`${ISOLATION_BACKEND}\` (experimental)\n`;
669
662
  message += '\n';
670
- message += '⚠️ *Note:* /solve, /do, /continue, /claude, /codex, /opencode, /agent, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats. /subscribe and /unsubscribe work in private and group chats.\n\n';
663
+ message += '⚠️ *Note:* /solve, /do, /continue, /claude, /codex, /opencode, /agent, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats. /terminal\\_watch, /subscribe and /unsubscribe work in private and group chats.\n\n';
671
664
  message += '🔧 *Common Options:*\n';
672
665
  message += `• \`--model <model>\` or \`-m\` - ${buildModelOptionDescription()}\n`;
673
666
  message += '• `--base-branch <branch>` or `-b` - Target branch for PR (default: repo default branch)\n';
@@ -763,7 +756,6 @@ bot.command('version', async ctx => {
763
756
  await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, '🤖 *Version Information*\n\n' + formatVersionMessage(result.versions), { parse_mode: 'Markdown' });
764
757
  });
765
758
 
766
- // Register external command modules (keeps telegram-bot.mjs under line limit)
767
759
  const { registerAcceptInvitesCommand } = await import('./telegram-accept-invitations.lib.mjs');
768
760
  const sharedCommandOpts = { VERBOSE, isOldMessage, isForwardedOrReply, isGroupChat: _isGroupChat, isChatAuthorized, isTopicAuthorized, buildAuthErrorMessage, addBreadcrumb, isChatStopped, getStoppedChatRejectMessage };
769
761
  registerAcceptInvitesCommand(bot, sharedCommandOpts);
@@ -1191,8 +1183,11 @@ bot.command(/^hive$/i, handleHiveCommand);
1191
1183
 
1192
1184
  const { registerTopCommand } = await import('./telegram-top-command.lib.mjs');
1193
1185
  const { registerStartStopCommands } = await import('./telegram-start-stop-command.lib.mjs');
1186
+ const { registerLogCommand } = await import('./telegram-log-command.lib.mjs');
1194
1187
  registerTopCommand(bot, sharedCommandOpts);
1195
1188
  registerStartStopCommands(bot, sharedCommandOpts);
1189
+ await registerLogCommand(bot, sharedCommandOpts);
1190
+ await registerTerminalWatchCommand(bot, sharedCommandOpts);
1196
1191
 
1197
1192
  // Add message listener for verbose debugging
1198
1193
  if (VERBOSE) {