bosun 0.36.0 → 0.36.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/.env.example +98 -16
  2. package/README.md +27 -0
  3. package/agent-event-bus.mjs +5 -5
  4. package/agent-pool.mjs +129 -12
  5. package/agent-prompts.mjs +7 -1
  6. package/agent-sdk.mjs +13 -2
  7. package/agent-supervisor.mjs +2 -2
  8. package/agent-work-report.mjs +1 -1
  9. package/anomaly-detector.mjs +6 -6
  10. package/autofix.mjs +15 -15
  11. package/bosun-skills.mjs +4 -4
  12. package/bosun.schema.json +160 -4
  13. package/claude-shell.mjs +11 -11
  14. package/cli.mjs +21 -21
  15. package/codex-config.mjs +19 -19
  16. package/codex-shell.mjs +180 -29
  17. package/config-doctor.mjs +27 -2
  18. package/config.mjs +60 -7
  19. package/copilot-shell.mjs +4 -4
  20. package/error-detector.mjs +1 -1
  21. package/fleet-coordinator.mjs +2 -2
  22. package/gemini-shell.mjs +692 -0
  23. package/github-oauth-portal.mjs +1 -1
  24. package/github-reconciler.mjs +2 -2
  25. package/kanban-adapter.mjs +741 -168
  26. package/merge-strategy.mjs +25 -25
  27. package/monitor.mjs +123 -105
  28. package/opencode-shell.mjs +22 -22
  29. package/package.json +7 -1
  30. package/postinstall.mjs +22 -22
  31. package/pr-cleanup-daemon.mjs +6 -6
  32. package/prepublish-check.mjs +4 -4
  33. package/presence.mjs +2 -2
  34. package/primary-agent.mjs +85 -7
  35. package/publish.mjs +1 -1
  36. package/review-agent.mjs +1 -1
  37. package/session-tracker.mjs +11 -0
  38. package/setup-web-server.mjs +429 -21
  39. package/setup.mjs +367 -12
  40. package/shared-knowledge.mjs +1 -1
  41. package/startup-service.mjs +9 -9
  42. package/stream-resilience.mjs +58 -4
  43. package/sync-engine.mjs +2 -2
  44. package/task-assessment.mjs +9 -9
  45. package/task-cli.mjs +1 -1
  46. package/task-complexity.mjs +71 -2
  47. package/task-context.mjs +1 -2
  48. package/task-executor.mjs +104 -41
  49. package/telegram-bot.mjs +825 -494
  50. package/telegram-sentinel.mjs +28 -28
  51. package/ui/app.js +256 -23
  52. package/ui/app.monolith.js +1 -1
  53. package/ui/components/agent-selector.js +4 -3
  54. package/ui/components/chat-view.js +101 -28
  55. package/ui/components/diff-viewer.js +3 -3
  56. package/ui/components/kanban-board.js +3 -3
  57. package/ui/components/session-list.js +255 -35
  58. package/ui/components/workspace-switcher.js +3 -3
  59. package/ui/demo.html +209 -194
  60. package/ui/index.html +3 -3
  61. package/ui/modules/icon-utils.js +206 -142
  62. package/ui/modules/icons.js +2 -27
  63. package/ui/modules/settings-schema.js +29 -5
  64. package/ui/modules/streaming.js +30 -2
  65. package/ui/modules/vision-stream.js +275 -0
  66. package/ui/modules/voice-client.js +102 -9
  67. package/ui/modules/voice-fallback.js +62 -6
  68. package/ui/modules/voice-overlay.js +594 -59
  69. package/ui/modules/voice.js +31 -38
  70. package/ui/setup.html +284 -34
  71. package/ui/styles/components.css +47 -0
  72. package/ui/styles/sessions.css +75 -0
  73. package/ui/tabs/agents.js +73 -43
  74. package/ui/tabs/chat.js +37 -40
  75. package/ui/tabs/control.js +2 -2
  76. package/ui/tabs/dashboard.js +1 -1
  77. package/ui/tabs/infra.js +10 -10
  78. package/ui/tabs/library.js +8 -8
  79. package/ui/tabs/logs.js +10 -10
  80. package/ui/tabs/settings.js +20 -20
  81. package/ui/tabs/tasks.js +76 -47
  82. package/ui-server.mjs +1761 -124
  83. package/update-check.mjs +13 -13
  84. package/ve-kanban.mjs +1 -1
  85. package/whatsapp-channel.mjs +5 -5
  86. package/workflow-engine.mjs +20 -1
  87. package/workflow-nodes.mjs +904 -4
  88. package/workflow-templates/agents.mjs +321 -7
  89. package/workflow-templates/ci-cd.mjs +6 -6
  90. package/workflow-templates/github.mjs +156 -84
  91. package/workflow-templates/planning.mjs +8 -8
  92. package/workflow-templates/reliability.mjs +8 -8
  93. package/workflow-templates/security.mjs +3 -3
  94. package/workflow-templates.mjs +15 -9
  95. package/workspace-manager.mjs +85 -1
  96. package/workspace-monitor.mjs +2 -2
  97. package/workspace-registry.mjs +2 -2
  98. package/worktree-manager.mjs +1 -1
package/monitor.mjs CHANGED
@@ -330,7 +330,7 @@ function formatAgentAlert(alert) {
330
330
  const severity = String(alert.severity || "medium").toUpperCase();
331
331
  const type = alert.type || "alert";
332
332
  const lines = [
333
- `🔎 Agent Analyzer: ${severity} ${type}`,
333
+ `:search: Agent Analyzer: ${severity} ${type}`,
334
334
  `Attempt: ${alert.attempt_id || "unknown"}`,
335
335
  ];
336
336
  if (alert.task_id) lines.push(`Task: ${alert.task_id}`);
@@ -525,10 +525,19 @@ async function ensureWorkflowAutomationEngine() {
525
525
  }
526
526
  : null;
527
527
 
528
+ let meetingService = null;
529
+ try {
530
+ const { createMeetingWorkflowService } = await import("./meeting-workflow-service.mjs");
531
+ meetingService = createMeetingWorkflowService();
532
+ } catch (err) {
533
+ console.warn(`[workflows] meeting service unavailable: ${err?.message || err}`);
534
+ }
535
+
528
536
  const services = {
529
537
  telegram: telegramService,
530
538
  kanban: kanbanService,
531
539
  agentPool: agentPoolService,
540
+ meeting: meetingService,
532
541
  prompts: agentPrompts || null,
533
542
  anomalyDetector: anomalyDetector || null,
534
543
  };
@@ -1122,7 +1131,7 @@ if (!isMainThread || chdirUnsupportedInRuntime) {
1122
1131
  if (r.success) {
1123
1132
  console.log(`[monitor] ✓ workspace repo ready: ${r.name}`);
1124
1133
  } else {
1125
- console.warn(`[monitor] workspace repo failed: ${r.name} — ${r.error}`);
1134
+ console.warn(`[monitor] :alert: workspace repo failed: ${r.name} — ${r.error}`);
1126
1135
  }
1127
1136
  }
1128
1137
  } catch (err) {
@@ -1436,7 +1445,7 @@ const workspaceMonitor = new WorkspaceMonitor({
1436
1445
  cacheDir: resolve(repoRoot, ".cache", "workspace-logs"),
1437
1446
  repoRoot,
1438
1447
  onStuckDetected: ({ attemptId, reason, recommendation }) => {
1439
- const msg = `⚠️ Agent ${attemptId.substring(0, 8)} stuck: ${reason}\nRecommendation: ${recommendation}`;
1448
+ const msg = `:alert: Agent ${attemptId.substring(0, 8)} stuck: ${reason}\nRecommendation: ${recommendation}`;
1440
1449
  console.warn(`[workspace-monitor] ${msg}`);
1441
1450
  void notify?.(msg, { dedupKey: `stuck-${attemptId.substring(0, 8)}` });
1442
1451
  },
@@ -1779,7 +1788,7 @@ function resolveMonitorMonitorTimeoutMs() {
1779
1788
  if (minTimeout !== null && maxTimeout !== null && maxTimeout < minTimeout) {
1780
1789
  warnMonitorTimeoutConfig(
1781
1790
  `bounds:${minTimeout}:${maxTimeout}`,
1782
- `[monitor] ⚠️ Invalid monitor-monitor timeout bounds: DEVMODE_MONITOR_MONITOR_TIMEOUT_MAX_MS=${maxTimeout}ms is lower than DEVMODE_MONITOR_MONITOR_TIMEOUT_MIN_MS=${minTimeout}ms. Ignoring max bound.`,
1791
+ `[monitor] :alert: Invalid monitor-monitor timeout bounds: DEVMODE_MONITOR_MONITOR_TIMEOUT_MAX_MS=${maxTimeout}ms is lower than DEVMODE_MONITOR_MONITOR_TIMEOUT_MIN_MS=${minTimeout}ms. Ignoring max bound.`,
1783
1792
  );
1784
1793
  maxTimeout = null;
1785
1794
  }
@@ -1795,7 +1804,7 @@ function resolveMonitorMonitorTimeoutMs() {
1795
1804
  if (legacyTimeout < MONITOR_MONITOR_RECOMMENDED_MIN_TIMEOUT_MS) {
1796
1805
  warnMonitorTimeoutConfig(
1797
1806
  `legacy-low:${legacyTimeout}`,
1798
- `[monitor] ⚠️ DEVMODE_AUTO_CODE_FIX_TIMEOUT_MS=${legacyTimeout}ms is low for monitor-monitor (recommended >= ${MONITOR_MONITOR_RECOMMENDED_MIN_TIMEOUT_MS}ms). Set DEVMODE_MONITOR_MONITOR_TIMEOUT_MS to override explicitly.`,
1807
+ `[monitor] :alert: DEVMODE_AUTO_CODE_FIX_TIMEOUT_MS=${legacyTimeout}ms is low for monitor-monitor (recommended >= ${MONITOR_MONITOR_RECOMMENDED_MIN_TIMEOUT_MS}ms). Set DEVMODE_MONITOR_MONITOR_TIMEOUT_MS to override explicitly.`,
1799
1808
  );
1800
1809
  }
1801
1810
  }
@@ -1810,7 +1819,7 @@ function resolveMonitorMonitorTimeoutMs() {
1810
1819
  if (timeoutMs < MONITOR_MONITOR_RECOMMENDED_MIN_TIMEOUT_MS) {
1811
1820
  warnMonitorTimeoutConfig(
1812
1821
  `effective-low:${timeoutMs}`,
1813
- `[monitor] ⚠️ monitor-monitor timeout is ${timeoutMs}ms. Values below ${MONITOR_MONITOR_RECOMMENDED_MIN_TIMEOUT_MS}ms can cause premature failover loops.`,
1822
+ `[monitor] :alert: monitor-monitor timeout is ${timeoutMs}ms. Values below ${MONITOR_MONITOR_RECOMMENDED_MIN_TIMEOUT_MS}ms can cause premature failover loops.`,
1814
1823
  );
1815
1824
  }
1816
1825
 
@@ -2267,7 +2276,7 @@ function tripCircuitBreaker(failureCount) {
2267
2276
  circuitBreakerResetAt = Date.now() + CIRCUIT_BREAKER_PAUSE_MS;
2268
2277
  const pauseMin = Math.round(CIRCUIT_BREAKER_PAUSE_MS / 60_000);
2269
2278
  console.error(
2270
- `[monitor] 🔌 CIRCUIT BREAKER TRIPPED: ${failureCount} failures in ${Math.round(CIRCUIT_BREAKER_WINDOW_MS / 1000)}s. ` +
2279
+ `[monitor] :plug: CIRCUIT BREAKER TRIPPED: ${failureCount} failures in ${Math.round(CIRCUIT_BREAKER_WINDOW_MS / 1000)}s. ` +
2271
2280
  `Killing orchestrator and pausing all restarts for ${pauseMin} minutes.`,
2272
2281
  );
2273
2282
 
@@ -2287,7 +2296,7 @@ function tripCircuitBreaker(failureCount) {
2287
2296
  if (!circuitBreakerNotified && telegramToken && telegramChatId) {
2288
2297
  circuitBreakerNotified = true;
2289
2298
  const msg =
2290
- `🔌 Circuit breaker tripped: ${failureCount} failures in ${Math.round(CIRCUIT_BREAKER_WINDOW_MS / 1000)}s.\n` +
2299
+ `:plug: Circuit breaker tripped: ${failureCount} failures in ${Math.round(CIRCUIT_BREAKER_WINDOW_MS / 1000)}s.\n` +
2291
2300
  `Orchestrator killed. All restarts paused for ${pauseMin} minutes.\n` +
2292
2301
  `Will auto-resume at ${new Date(circuitBreakerResetAt).toLocaleTimeString()}.`;
2293
2302
  // Fire-and-forget with skipDedup to ensure it gets through
@@ -2664,7 +2673,7 @@ async function handleMonitorFailure(reason, err) {
2664
2673
  const pauseMs = Math.max(orchestratorPauseMs, 30 * 60 * 1000);
2665
2674
  const pauseMin = Math.max(1, Math.round(pauseMs / 60_000));
2666
2675
  const msg =
2667
- `🛑 bosun hit hard failure cap (${failureCount}). ` +
2676
+ `:close: bosun hit hard failure cap (${failureCount}). ` +
2668
2677
  `Entering safe mode for ${pauseMin} minute(s); monitor process will stay alive.`;
2669
2678
  console.error(`[monitor] ${msg}`);
2670
2679
  if (telegramToken && telegramChatId) {
@@ -2713,7 +2722,7 @@ async function handleMonitorFailure(reason, err) {
2713
2722
  try {
2714
2723
  const shortMsg = message.length > 200 ? message.slice(0, 200) + "…" : message;
2715
2724
  await sendTelegramMessage(
2716
- `⚠️ bosun exception (${reason}): ${shortMsg}\n\nAttempting recovery (count=${failureCount}).`,
2725
+ `:alert: bosun exception (${reason}): ${shortMsg}\n\nAttempting recovery (count=${failureCount}).`,
2717
2726
  );
2718
2727
  } catch {
2719
2728
  /* suppress Telegram errors during failure handling */
@@ -2729,7 +2738,7 @@ async function handleMonitorFailure(reason, err) {
2729
2738
  if (telegramToken && telegramChatId) {
2730
2739
  try {
2731
2740
  await sendTelegramMessage(
2732
- `🛠️ bosun auto-fix applied. Restarting monitor.\n${fixResult.outcome}`,
2741
+ `:u1f6e0: bosun auto-fix applied. Restarting monitor.\n${fixResult.outcome}`,
2733
2742
  );
2734
2743
  } catch {
2735
2744
  /* best effort */
@@ -2745,7 +2754,7 @@ async function handleMonitorFailure(reason, err) {
2745
2754
  if (telegramToken && telegramChatId) {
2746
2755
  try {
2747
2756
  await sendTelegramMessage(
2748
- `🛑 bosun entering safe mode after repeated failures (${failureCount} in 10m). Pausing restarts for ${pauseMin} minutes.`,
2757
+ `:close: bosun entering safe mode after repeated failures (${failureCount} in 10m). Pausing restarts for ${pauseMin} minutes.`,
2749
2758
  );
2750
2759
  } catch {
2751
2760
  /* best effort */
@@ -3039,7 +3048,7 @@ function triggerLoopFix(errorLine, repeatCount) {
3039
3048
  } catch (err) {
3040
3049
  console.warn(`[monitor] loop fix error: ${err.message || err}`);
3041
3050
  if (telegramFn) {
3042
- telegramFn(`🔁 Loop fix crashed: ${err.message || err}`);
3051
+ telegramFn(`:repeat: Loop fix crashed: ${err.message || err}`);
3043
3052
  }
3044
3053
  } finally {
3045
3054
  loopFixInProgress = false;
@@ -3553,10 +3562,10 @@ function ensureAnomalyDetector() {
3553
3562
  onAnomaly: wrapAnomalyCallback((anomaly) => {
3554
3563
  const icon =
3555
3564
  anomaly.severity === "CRITICAL"
3556
- ? "🔴"
3565
+ ? ":dot:"
3557
3566
  : anomaly.severity === "HIGH"
3558
- ? "🟠"
3559
- : "🟡";
3567
+ ? ":u1f7e0:"
3568
+ : ":dot:";
3560
3569
  console.warn(
3561
3570
  `[anomaly-detector] ${icon} ${anomaly.severity} ${anomaly.type} [${anomaly.shortId}]: ${anomaly.message}`,
3562
3571
  );
@@ -3742,7 +3751,7 @@ function ensureVkLogStream() {
3742
3751
  );
3743
3752
 
3744
3753
  // Notify via Telegram
3745
- const emoji = resolution.result.success ? "🤖" : "⚠️";
3754
+ const emoji = resolution.result.success ? ":bot:" : ":alert:";
3746
3755
  const status = resolution.result.success ? "resolved" : "failed";
3747
3756
  const branch =
3748
3757
  resolution.context.branch || `PR #${resolution.context.prNumber}`;
@@ -4729,7 +4738,7 @@ async function startFreshSession(workspaceId, prompt, taskId) {
4729
4738
  }
4730
4739
 
4731
4740
  console.log(
4732
- `[monitor] Fresh session started: ${session.id} (retry #${freshSessionCount})`,
4741
+ `[monitor] :check: Fresh session started: ${session.id} (retry #${freshSessionCount})`,
4733
4742
  );
4734
4743
 
4735
4744
  // Connect the VK log stream to this session for real-time log capture
@@ -4798,7 +4807,7 @@ async function attemptFreshSessionRetry(reason, logTail) {
4798
4807
  const taskLabel =
4799
4808
  attemptInfo.task_title || attemptInfo.branch || "unknown";
4800
4809
  void sendTelegramMessage(
4801
- `🔄 Fresh session started for "${taskLabel}" (${reason}).\nNew session: ${result.sessionId}`,
4810
+ `:refresh: Fresh session started for "${taskLabel}" (${reason}).\nNew session: ${result.sessionId}`,
4802
4811
  );
4803
4812
  }
4804
4813
  return true;
@@ -4807,7 +4816,7 @@ async function attemptFreshSessionRetry(reason, logTail) {
4807
4816
  console.warn(`[monitor] fresh session retry failed: ${result.reason}`);
4808
4817
  if (telegramToken && telegramChatId) {
4809
4818
  void sendTelegramMessage(
4810
- `⚠️ Fresh session retry failed (${reason}): ${result.reason}`,
4819
+ `:alert: Fresh session retry failed (${reason}): ${result.reason}`,
4811
4820
  );
4812
4821
  }
4813
4822
  return false;
@@ -5279,7 +5288,7 @@ async function safeRecoverTask(taskId, taskTitle, reason) {
5279
5288
  const success = await updateTaskStatus(taskId, "todo");
5280
5289
  if (success) {
5281
5290
  console.log(
5282
- `[monitor] ♻️ Recovered "${taskTitle}" from ${localStatus || "inprogress"} → todo (${reason}) [${activeBackend} backend - VK status re-fetch skipped]`,
5291
+ `[monitor] :repeat: Recovered "${taskTitle}" from ${localStatus || "inprogress"} → todo (${reason}) [${activeBackend} backend - VK status re-fetch skipped]`,
5283
5292
  );
5284
5293
  } else {
5285
5294
  console.warn(
@@ -5348,11 +5357,11 @@ async function safeRecoverTask(taskId, taskTitle, reason) {
5348
5357
  if (success) {
5349
5358
  if (isInternal) {
5350
5359
  console.log(
5351
- `[monitor] ♻️ Recovered "${taskTitle}" from ${liveStatus} → todo (${reason}) [internal mode — VK session skipped]`,
5360
+ `[monitor] :repeat: Recovered "${taskTitle}" from ${liveStatus} → todo (${reason}) [internal mode — VK session skipped]`,
5352
5361
  );
5353
5362
  } else {
5354
5363
  console.log(
5355
- `[monitor] ♻️ Recovered "${taskTitle}" from ${liveStatus} → todo (${reason})`,
5364
+ `[monitor] :repeat: Recovered "${taskTitle}" from ${liveStatus} → todo (${reason})`,
5356
5365
  );
5357
5366
  }
5358
5367
  }
@@ -6382,11 +6391,11 @@ async function checkMergedPRsAndUpdateTasks() {
6382
6391
  completedTaskNames.push(task.title);
6383
6392
  if (success) {
6384
6393
  console.log(
6385
- `[monitor] Moved task "${task.title}" from ${taskStatus} → done`,
6394
+ `[monitor] :check: Moved task "${task.title}" from ${taskStatus} → done`,
6386
6395
  );
6387
6396
  } else {
6388
6397
  console.warn(
6389
- `[monitor] ⚠️ VK update failed for "${task.title}" — cached anyway (PR is merged)`,
6398
+ `[monitor] :alert: VK update failed for "${task.title}" — cached anyway (PR is merged)`,
6390
6399
  );
6391
6400
  }
6392
6401
  // ── Trigger downstream rebase for tasks on same upstream ──
@@ -6467,11 +6476,11 @@ async function checkMergedPRsAndUpdateTasks() {
6467
6476
  completedTaskNames.push(task.title);
6468
6477
  if (success) {
6469
6478
  console.log(
6470
- `[monitor] Moved task "${task.title}" from ${taskStatus} → done`,
6479
+ `[monitor] :check: Moved task "${task.title}" from ${taskStatus} → done`,
6471
6480
  );
6472
6481
  } else {
6473
6482
  console.warn(
6474
- `[monitor] ⚠️ VK update failed for "${task.title}" — cached anyway (branch is merged)`,
6483
+ `[monitor] :alert: VK update failed for "${task.title}" — cached anyway (branch is merged)`,
6475
6484
  );
6476
6485
  }
6477
6486
  // ── Trigger downstream rebase for tasks on same upstream ──
@@ -6559,7 +6568,7 @@ async function checkMergedPRsAndUpdateTasks() {
6559
6568
  const attempts = conflictResolutionAttempts.get(task.id) || 0;
6560
6569
  if (attempts >= CONFLICT_MAX_ATTEMPTS) {
6561
6570
  console.warn(
6562
- `[monitor] ⚠️ Task "${task.title}" PR #${conflictCandidates[0].prNumber} conflict resolution exhausted (${attempts}/${CONFLICT_MAX_ATTEMPTS} attempts) — skipping`,
6571
+ `[monitor] :alert: Task "${task.title}" PR #${conflictCandidates[0].prNumber} conflict resolution exhausted (${attempts}/${CONFLICT_MAX_ATTEMPTS} attempts) — skipping`,
6563
6572
  );
6564
6573
  } else {
6565
6574
  conflictResolutionAttempts.set(task.id, attempts + 1);
@@ -6581,11 +6590,11 @@ async function checkMergedPRsAndUpdateTasks() {
6581
6590
 
6582
6591
  if (!sdkOnCooldown && !sdkExhausted) {
6583
6592
  console.log(
6584
- `[monitor] ⚠️ Task "${task.title}" PR #${cc.prNumber} has merge conflicts — launching SDK resolver (attempt ${shortId})`,
6593
+ `[monitor] :alert: Task "${task.title}" PR #${cc.prNumber} has merge conflicts — launching SDK resolver (attempt ${shortId})`,
6585
6594
  );
6586
6595
  if (telegramToken && telegramChatId) {
6587
6596
  void sendTelegramMessage(
6588
- `🔀 PR #${cc.prNumber} for "${task.title}" has merge conflicts — launching SDK resolver (attempt ${shortId})`,
6597
+ `:git: PR #${cc.prNumber} for "${task.title}" has merge conflicts — launching SDK resolver (attempt ${shortId})`,
6589
6598
  );
6590
6599
  }
6591
6600
 
@@ -6635,23 +6644,23 @@ async function checkMergedPRsAndUpdateTasks() {
6635
6644
  });
6636
6645
  if (result.success) {
6637
6646
  console.log(
6638
- `[monitor] SDK resolved conflicts for PR #${cc.prNumber} (${result.resolvedFiles.length} files)`,
6647
+ `[monitor] :check: SDK resolved conflicts for PR #${cc.prNumber} (${result.resolvedFiles.length} files)`,
6639
6648
  );
6640
6649
  clearDirtyTask(task.id);
6641
6650
  clearSDKResolutionState(cc.branch);
6642
6651
  conflictResolutionAttempts.delete(task.id); // Reset on success
6643
6652
  if (telegramToken && telegramChatId) {
6644
6653
  void sendTelegramMessage(
6645
- `✅ SDK resolved merge conflicts for PR #${cc.prNumber} "${task.title}" (${result.resolvedFiles.length} files)`,
6654
+ `:check: SDK resolved merge conflicts for PR #${cc.prNumber} "${task.title}" (${result.resolvedFiles.length} files)`,
6646
6655
  );
6647
6656
  }
6648
6657
  } else {
6649
6658
  console.warn(
6650
- `[monitor] SDK conflict resolution failed for PR #${cc.prNumber}: ${result.error}`,
6659
+ `[monitor] :close: SDK conflict resolution failed for PR #${cc.prNumber}: ${result.error}`,
6651
6660
  );
6652
6661
  if (telegramToken && telegramChatId) {
6653
6662
  void sendTelegramMessage(
6654
- `❌ SDK conflict resolution failed for PR #${cc.prNumber} "${task.title}": ${result.error}\nFalling back to orchestrator.`,
6663
+ `:close: SDK conflict resolution failed for PR #${cc.prNumber} "${task.title}": ${result.error}\nFalling back to orchestrator.`,
6655
6664
  );
6656
6665
  }
6657
6666
  conflictsTriggered++;
@@ -6669,7 +6678,7 @@ async function checkMergedPRsAndUpdateTasks() {
6669
6678
  );
6670
6679
  if (telegramToken && telegramChatId) {
6671
6680
  void sendTelegramMessage(
6672
- `🔀 PR #${cc.prNumber} for "${task.title}" has merge conflicts — no worktree, orchestrator will handle (attempt ${shortId})`,
6681
+ `:git: PR #${cc.prNumber} for "${task.title}" has merge conflicts — no worktree, orchestrator will handle (attempt ${shortId})`,
6673
6682
  );
6674
6683
  }
6675
6684
  conflictsTriggered++;
@@ -6680,11 +6689,11 @@ async function checkMergedPRsAndUpdateTasks() {
6680
6689
  ? "SDK attempts exhausted"
6681
6690
  : "SDK on cooldown";
6682
6691
  console.log(
6683
- `[monitor] ⚠️ Task "${task.title}" PR #${cc.prNumber} has merge conflicts — ${reason}, deferring to orchestrator (attempt ${shortId})`,
6692
+ `[monitor] :alert: Task "${task.title}" PR #${cc.prNumber} has merge conflicts — ${reason}, deferring to orchestrator (attempt ${shortId})`,
6684
6693
  );
6685
6694
  if (telegramToken && telegramChatId) {
6686
6695
  void sendTelegramMessage(
6687
- `🔀 PR #${cc.prNumber} for "${task.title}" has merge conflicts — ${reason}, orchestrator will handle (attempt ${shortId})`,
6696
+ `:git: PR #${cc.prNumber} for "${task.title}" has merge conflicts — ${reason}, orchestrator will handle (attempt ${shortId})`,
6688
6697
  );
6689
6698
  }
6690
6699
  conflictsTriggered++;
@@ -6713,7 +6722,7 @@ async function checkMergedPRsAndUpdateTasks() {
6713
6722
  if (success) {
6714
6723
  movedReviewCount++;
6715
6724
  console.log(
6716
- `[monitor] Moved task "${task.title}" from ${taskStatus} → inreview`,
6725
+ `[monitor] :check: Moved task "${task.title}" from ${taskStatus} → inreview`,
6717
6726
  );
6718
6727
  }
6719
6728
  } else if (!hasOpenPR) {
@@ -6789,7 +6798,7 @@ async function checkMergedPRsAndUpdateTasks() {
6789
6798
  if (movedCount <= 3) {
6790
6799
  // Few tasks — list them individually
6791
6800
  for (const name of completedTaskNames) {
6792
- void sendTelegramMessage(`✅ Task completed: "${name}"`);
6801
+ void sendTelegramMessage(`:check: Task completed: "${name}"`);
6793
6802
  }
6794
6803
  } else {
6795
6804
  // Many tasks — send a single summary to avoid spam
@@ -6799,7 +6808,7 @@ async function checkMergedPRsAndUpdateTasks() {
6799
6808
  .join("\n");
6800
6809
  const extra = movedCount > 5 ? `\n…and ${movedCount - 5} more` : "";
6801
6810
  void sendTelegramMessage(
6802
- `✅ ${movedCount} tasks moved to done:\n${listed}${extra}`,
6811
+ `:check: ${movedCount} tasks moved to done:\n${listed}${extra}`,
6803
6812
  );
6804
6813
  }
6805
6814
  }
@@ -6827,7 +6836,7 @@ async function checkMergedPRsAndUpdateTasks() {
6827
6836
  if (movedTodoCount <= 3) {
6828
6837
  for (const name of recoveredTaskNames) {
6829
6838
  void sendTelegramMessage(
6830
- `♻️ Task recovered to todo (abandoned — no branch/PR): "${name}"`,
6839
+ `:repeat: Task recovered to todo (abandoned — no branch/PR): "${name}"`,
6831
6840
  );
6832
6841
  }
6833
6842
  } else {
@@ -6838,7 +6847,7 @@ async function checkMergedPRsAndUpdateTasks() {
6838
6847
  const extra =
6839
6848
  movedTodoCount > 5 ? `\n…and ${movedTodoCount - 5} more` : "";
6840
6849
  void sendTelegramMessage(
6841
- `♻️ ${movedTodoCount} abandoned tasks recovered to todo:\n${listed}${extra}`,
6850
+ `:repeat: ${movedTodoCount} abandoned tasks recovered to todo:\n${listed}${extra}`,
6842
6851
  );
6843
6852
  }
6844
6853
  }
@@ -7444,7 +7453,7 @@ async function checkEpicBranches(reason = "interval") {
7444
7453
  null,
7445
7454
  );
7446
7455
  void sendTelegramMessage(
7447
- `⚠️ Epic sync conflict on ${epicBranch} → ${DEFAULT_TARGET_BRANCH} (${reason})`,
7456
+ `:alert: Epic sync conflict on ${epicBranch} → ${DEFAULT_TARGET_BRANCH} (${reason})`,
7448
7457
  );
7449
7458
  }
7450
7459
  continue;
@@ -7475,7 +7484,7 @@ async function checkEpicBranches(reason = "interval") {
7475
7484
  prUrl: created.url,
7476
7485
  });
7477
7486
  void sendTelegramMessage(
7478
- `🧩 Epic PR created for ${epicBranch} → ${DEFAULT_TARGET_BRANCH}\n${created.url}`,
7487
+ `:workflow: Epic PR created for ${epicBranch} → ${DEFAULT_TARGET_BRANCH}\n${created.url}`,
7479
7488
  );
7480
7489
  } else if (created?.skipped) {
7481
7490
  updateEpicMergeCache(cacheKey, {
@@ -7518,7 +7527,7 @@ async function checkEpicBranches(reason = "interval") {
7518
7527
  prInfo,
7519
7528
  );
7520
7529
  void sendTelegramMessage(
7521
- `⚠️ Epic PR conflicts for ${epicBranch} → ${DEFAULT_TARGET_BRANCH} (${prInfo.url || "no url"})`,
7530
+ `:alert: Epic PR conflicts for ${epicBranch} → ${DEFAULT_TARGET_BRANCH} (${prInfo.url || "no url"})`,
7522
7531
  );
7523
7532
  continue;
7524
7533
  }
@@ -7541,7 +7550,7 @@ async function checkEpicBranches(reason = "interval") {
7541
7550
  prInfo,
7542
7551
  );
7543
7552
  void sendTelegramMessage(
7544
- `⚠️ Epic PR checks failing for ${epicBranch} → ${DEFAULT_TARGET_BRANCH} (${prInfo.url || "no url"})`,
7553
+ `:alert: Epic PR checks failing for ${epicBranch} → ${DEFAULT_TARGET_BRANCH} (${prInfo.url || "no url"})`,
7545
7554
  );
7546
7555
  continue;
7547
7556
  }
@@ -7717,9 +7726,9 @@ async function checkAndMergeDependabotPRs() {
7717
7726
  encoding: "utf8",
7718
7727
  timeout: 30_000,
7719
7728
  });
7720
- console.log(`[dependabot] PR #${pr.number} merged: ${pr.title}`);
7729
+ console.log(`[dependabot] :check: PR #${pr.number} merged: ${pr.title}`);
7721
7730
  void sendTelegramMessage(
7722
- `✅ Auto-merged bot PR #${pr.number}: ${pr.title}`,
7731
+ `:check: Auto-merged bot PR #${pr.number}: ${pr.title}`,
7723
7732
  );
7724
7733
  } catch (mergeErr) {
7725
7734
  const errMsg = mergeErr.stderr || mergeErr.message || "";
@@ -7732,7 +7741,7 @@ async function checkAndMergeDependabotPRs() {
7732
7741
  `[dependabot] PR #${pr.number}: auto-merge enabled, will merge when protection rules are met`,
7733
7742
  );
7734
7743
  void sendTelegramMessage(
7735
- `🔄 Auto-merge enabled for bot PR #${pr.number}: ${pr.title}`,
7744
+ `:refresh: Auto-merge enabled for bot PR #${pr.number}: ${pr.title}`,
7736
7745
  );
7737
7746
  }
7738
7747
  }
@@ -8278,7 +8287,7 @@ async function rebaseDownstreamTasks(mergedUpstreamBranch, excludeAttemptId) {
8278
8287
  const summary = `Downstream rebase after merge to ${mergedUpstreamBranch}: ${rebasedCount} rebased, ${failedCount} failed`;
8279
8288
  console.log(`[${tag}] ${summary}`);
8280
8289
  void sendTelegramMessage(
8281
- `🔄 ${summary}\n${rebaseResults.map((r) => ` ${r.status === "success" ? "✓" : "✗"} ${r.taskTitle}`).join("\n")}`,
8290
+ `:refresh: ${summary}\n${rebaseResults.map((r) => ` ${r.status === "success" ? "✓" : "✗"} ${r.taskTitle}`).join("\n")}`,
8282
8291
  );
8283
8292
  } else {
8284
8293
  console.log(
@@ -8396,7 +8405,7 @@ async function actOnAssessment(ctx, decision) {
8396
8405
  await updateTaskStatus(ctx.taskId, "todo");
8397
8406
  }
8398
8407
  void sendTelegramMessage(
8399
- `🆕 Assessment: starting new attempt for "${ctx.taskTitle}" — ${decision.reason || ""}`,
8408
+ `:star: Assessment: starting new attempt for "${ctx.taskTitle}" — ${decision.reason || ""}`,
8400
8409
  );
8401
8410
  break;
8402
8411
 
@@ -8415,7 +8424,7 @@ async function actOnAssessment(ctx, decision) {
8415
8424
  case "manual_review":
8416
8425
  console.log(`[${tag}] → manual review`);
8417
8426
  void sendTelegramMessage(
8418
- `👀 Assessment: manual review needed for "${ctx.taskTitle}" — ${decision.reason || ""}`,
8427
+ `:eye: Assessment: manual review needed for "${ctx.taskTitle}" — ${decision.reason || ""}`,
8419
8428
  );
8420
8429
  break;
8421
8430
 
@@ -8425,7 +8434,7 @@ async function actOnAssessment(ctx, decision) {
8425
8434
  await updateTaskStatus(ctx.taskId, "todo");
8426
8435
  }
8427
8436
  void sendTelegramMessage(
8428
- `🚫 Assessment: closing and replanning "${ctx.taskTitle}" — ${decision.reason || ""}`,
8437
+ `:ban: Assessment: closing and replanning "${ctx.taskTitle}" — ${decision.reason || ""}`,
8429
8438
  );
8430
8439
  break;
8431
8440
 
@@ -8866,7 +8875,7 @@ async function smartPRFlow(attemptId, shortId, status) {
8866
8875
  ? ` Examples: ${verify.sampleTitles.join(", ")}`
8867
8876
  : "";
8868
8877
  void sendTelegramMessage(
8869
- `✅ Task planner verified: ${verify.createdCount} new task(s) detected.${suffix}`,
8878
+ `:check: Task planner verified: ${verify.createdCount} new task(s) detected.${suffix}`,
8870
8879
  );
8871
8880
  }
8872
8881
  return;
@@ -8878,7 +8887,7 @@ async function smartPRFlow(attemptId, shortId, status) {
8878
8887
  await archiveAttempt(attemptId);
8879
8888
  if (telegramToken && telegramChatId) {
8880
8889
  void sendTelegramMessage(
8881
- "⚠️ Task planner incomplete: no new backlog tasks detected. Returned to todo.",
8890
+ ":alert: Task planner incomplete: no new backlog tasks detected. Returned to todo.",
8882
8891
  );
8883
8892
  }
8884
8893
  return;
@@ -8920,7 +8929,7 @@ async function smartPRFlow(attemptId, shortId, status) {
8920
8929
  await archiveAttempt(attemptId);
8921
8930
  if (telegramToken && telegramChatId) {
8922
8931
  void sendTelegramMessage(
8923
- `🗑️ Archived attempt ${shortId}: no commits, no changes (status=${status}). Task will be reattempted.`,
8932
+ `:trash: Archived attempt ${shortId}: no commits, no changes (status=${status}). Task will be reattempted.`,
8924
8933
  );
8925
8934
  }
8926
8935
  return;
@@ -8985,7 +8994,7 @@ async function smartPRFlow(attemptId, shortId, status) {
8985
8994
  ? "Fresh session started for reattempt."
8986
8995
  : "Will reattempt on next cycle.";
8987
8996
  void sendTelegramMessage(
8988
- `🗑️ Archived stale attempt ${shortId} after failed rebase. ${action}`,
8997
+ `:trash: Archived stale attempt ${shortId} after failed rebase. ${action}`,
8989
8998
  );
8990
8999
  }
8991
9000
  return;
@@ -9083,7 +9092,7 @@ Return a short summary of what you did and any files that needed manual resoluti
9083
9092
  );
9084
9093
  if (telegramToken && telegramChatId) {
9085
9094
  void sendTelegramMessage(
9086
- `✅ Codex resolved rebase conflicts for ${shortId}. Log: ${logPath}`,
9095
+ `:check: Codex resolved rebase conflicts for ${shortId}. Log: ${logPath}`,
9087
9096
  );
9088
9097
  }
9089
9098
  return;
@@ -9093,7 +9102,7 @@ Return a short summary of what you did and any files that needed manual resoluti
9093
9102
  );
9094
9103
  if (telegramToken && telegramChatId) {
9095
9104
  void sendTelegramMessage(
9096
- `⚠️ Codex failed to resolve conflicts for ${shortId}. Log: ${logPath}`,
9105
+ `:alert: Codex failed to resolve conflicts for ${shortId}. Log: ${logPath}`,
9097
9106
  );
9098
9107
  }
9099
9108
  }
@@ -9103,7 +9112,7 @@ Return a short summary of what you did and any files that needed manual resoluti
9103
9112
  );
9104
9113
  if (telegramToken && telegramChatId) {
9105
9114
  void sendTelegramMessage(
9106
- `⚠️ Attempt ${shortId} has unresolvable rebase conflicts: ${files.join(", ")}`,
9115
+ `:alert: Attempt ${shortId} has unresolvable rebase conflicts: ${files.join(", ")}`,
9107
9116
  );
9108
9117
  }
9109
9118
  if (primaryAgentReady) {
@@ -9159,7 +9168,7 @@ Return a short summary of what you did and any files that needed manual resoluti
9159
9168
  );
9160
9169
  if (telegramToken && telegramChatId) {
9161
9170
  void sendTelegramMessage(
9162
- `⚠️ Auto-PR skipped for ${shortId}: existing PR #${existingPr.number} (${state}) already linked to ${branchName}.`,
9171
+ `:alert: Auto-PR skipped for ${shortId}: existing PR #${existingPr.number} (${state}) already linked to ${branchName}.`,
9163
9172
  );
9164
9173
  }
9165
9174
  return;
@@ -9184,7 +9193,7 @@ Return a short summary of what you did and any files that needed manual resoluti
9184
9193
  );
9185
9194
  if (telegramToken && telegramChatId) {
9186
9195
  void sendTelegramMessage(
9187
- `✅ Auto-created PR for ${shortId}${prUrl ? ": " + prUrl : ""}`,
9196
+ `:check: Auto-created PR for ${shortId}${prUrl ? ": " + prUrl : ""}`,
9188
9197
  );
9189
9198
  }
9190
9199
 
@@ -9221,7 +9230,7 @@ Return a short summary of what you did and any files that needed manual resoluti
9221
9230
  );
9222
9231
  if (telegramToken && telegramChatId) {
9223
9232
  void sendTelegramMessage(
9224
- `⚠️ Auto-PR for ${shortId} failed: repo_id missing. Check VK_BASE_URL/VK_REPO_ID.`,
9233
+ `:alert: Auto-PR for ${shortId} failed: repo_id missing. Check VK_BASE_URL/VK_REPO_ID.`,
9225
9234
  );
9226
9235
  }
9227
9236
  return;
@@ -9234,7 +9243,7 @@ Return a short summary of what you did and any files that needed manual resoluti
9234
9243
  );
9235
9244
  if (telegramToken && telegramChatId) {
9236
9245
  void sendTelegramMessage(
9237
- `⚠️ Auto-PR for ${shortId} fast-failed (${elapsed}ms) — likely worktree issue. Prompting agent.`,
9246
+ `:alert: Auto-PR for ${shortId} fast-failed (${elapsed}ms) — likely worktree issue. Prompting agent.`,
9238
9247
  );
9239
9248
  }
9240
9249
  if (primaryAgentReady) {
@@ -9257,7 +9266,7 @@ Return a short summary of what you did and any files that needed manual resoluti
9257
9266
  );
9258
9267
  if (telegramToken && telegramChatId) {
9259
9268
  void sendTelegramMessage(
9260
- `⚠️ Auto-PR for ${shortId} failed after ${Math.round(elapsed / 1000)}s (prepush hooks). Prompting agent to fix.`,
9269
+ `:alert: Auto-PR for ${shortId} failed after ${Math.round(elapsed / 1000)}s (prepush hooks). Prompting agent to fix.`,
9261
9270
  );
9262
9271
  }
9263
9272
  if (primaryAgentReady) {
@@ -9876,7 +9885,10 @@ function buildPlannerTaskDescription({
9876
9885
  " its dedicated branch and integrates upstream changes continuously.",
9877
9886
  " Examples: `feat(veid):` → `origin/veid`, `fix(market):` → `origin/market`.",
9878
9887
  " Do NOT set base_branch for cross-cutting tasks that modify many modules.",
9879
- "8. If a task should target a non-default epic/base branch for other reasons, include `base_branch` in the JSON task object.",].join("\n");
9888
+ "8. If a task should target a non-default epic/base branch for other reasons, include `base_branch` in the JSON task object.",
9889
+ "9. Output MUST be exactly one fenced ```json code block with shape { \"tasks\": [...] } and no surrounding prose.",
9890
+ "10. Each task object must include title, description, implementation_steps, acceptance_criteria, verification.",
9891
+ "11. Do not output placeholder tasks. If uncertain, reduce scope but keep tasks executable.",].join("\n");
9880
9892
  }
9881
9893
 
9882
9894
  function normalizePlannerTitleForComparison(title) {
@@ -10100,7 +10112,7 @@ function buildTaskPlannerStatusText(plannerState, reason = "interval") {
10100
10112
  ? formatElapsedMs(now - Date.parse(plannerState.last_success_at))
10101
10113
  : "never";
10102
10114
  return [
10103
- "📋 Codex-Task-Planner Update",
10115
+ ":clipboard: Codex-Task-Planner Update",
10104
10116
  `- Reason: ${reason}`,
10105
10117
  `- Planner mode: ${plannerMode}`,
10106
10118
  `- Trigger in progress: ${plannerTriggered ? "yes" : "no"}`,
@@ -10224,7 +10236,7 @@ async function sendTelegramMessage(text, options = {}) {
10224
10236
  let priority = 4; // default: info
10225
10237
  let category = "general";
10226
10238
 
10227
- // Positive signals override negative keyword matches — a " Task completed"
10239
+ // Positive signals override negative keyword matches — a ":check: Task completed"
10228
10240
  // message should never be classified as an error even when the task title
10229
10241
  // happens to contain words like "error" or "failed".
10230
10242
  // Orchestrator periodic updates contain counter labels like "Failed: 0" and
@@ -10232,7 +10244,7 @@ async function sendTelegramMessage(text, options = {}) {
10232
10244
  // Status updates (planner, monitor-monitor) contain "Last error: none" which
10233
10245
  // is informational, not an actual error.
10234
10246
  const isPositive =
10235
- textLower.includes("") ||
10247
+ textLower.includes(":check:") ||
10236
10248
  textLower.includes("task completed") ||
10237
10249
  textLower.includes("branch merged") ||
10238
10250
  textLower.includes("pr merged") ||
@@ -10245,7 +10257,7 @@ async function sendTelegramMessage(text, options = {}) {
10245
10257
  !isPositive &&
10246
10258
  (textLower.includes("fatal") ||
10247
10259
  textLower.includes("critical") ||
10248
- textLower.includes("🔥"))
10260
+ textLower.includes(":zap:"))
10249
10261
  ) {
10250
10262
  priority = 1;
10251
10263
  category = "critical";
@@ -10255,7 +10267,7 @@ async function sendTelegramMessage(text, options = {}) {
10255
10267
  !isPositive &&
10256
10268
  (textLower.includes("error") ||
10257
10269
  textLower.includes("failed") ||
10258
- textLower.includes("") ||
10270
+ textLower.includes(":close:") ||
10259
10271
  textLower.includes("auto-fix gave up"))
10260
10272
  ) {
10261
10273
  priority = 2;
@@ -10264,7 +10276,7 @@ async function sendTelegramMessage(text, options = {}) {
10264
10276
  // Priority 3: Warnings
10265
10277
  else if (
10266
10278
  !isPositive &&
10267
- (textLower.includes("warning") || textLower.includes("⚠️"))
10279
+ (textLower.includes("warning") || textLower.includes(":alert:"))
10268
10280
  ) {
10269
10281
  priority = 3;
10270
10282
  category = "warning";
@@ -10346,7 +10358,7 @@ async function maybeSendWeeklyReport(nowInput = new Date()) {
10346
10358
  } catch (err) {
10347
10359
  console.warn(`[monitor] weekly report generation failed: ${err?.message || err}`);
10348
10360
  await sendTelegramMessage(
10349
- `⚠️ Weekly report failed: ${err?.message || err}`,
10361
+ `:alert: Weekly report failed: ${err?.message || err}`,
10350
10362
  { dedupKey: "weekly-report:failed", exactDedup: true },
10351
10363
  );
10352
10364
  }
@@ -10357,12 +10369,12 @@ globalThis.__bosunNotifyAnomaly = (anomaly) => {
10357
10369
  if (!telegramToken || !telegramChatId) return;
10358
10370
  const icon =
10359
10371
  anomaly.severity === "CRITICAL"
10360
- ? "🔴"
10372
+ ? ":dot:"
10361
10373
  : anomaly.severity === "HIGH"
10362
- ? "🟠"
10374
+ ? ":u1f7e0:"
10363
10375
  : anomaly.severity === "MEDIUM"
10364
- ? "🟡"
10365
- : "⚪️";
10376
+ ? ":dot:"
10377
+ : ":dot:";
10366
10378
  const lines = [
10367
10379
  `${icon} Internal Anomaly: ${anomaly.type}`,
10368
10380
  `Attempt: ${anomaly.processId || anomaly.shortId || "unknown"}`,
@@ -10963,7 +10975,7 @@ async function checkStatusMilestones() {
10963
10975
  if (!allCompleteNotified) {
10964
10976
  allCompleteNotified = true;
10965
10977
  await sendTelegramMessage(
10966
- `🛰️ Fleet entering maintenance mode: ${maintenance.reason}`,
10978
+ `:server: Fleet entering maintenance mode: ${maintenance.reason}`,
10967
10979
  );
10968
10980
  }
10969
10981
  return;
@@ -11319,7 +11331,7 @@ async function triggerTaskPlanner(
11319
11331
  );
11320
11332
  if (notify) {
11321
11333
  await sendTelegramMessage(
11322
- `⚠️ Task planner kanban path failed on ${backend}; using codex fallback.\nReason: ${message}`,
11334
+ `:alert: Task planner kanban path failed on ${backend}; using codex fallback.\nReason: ${message}`,
11323
11335
  );
11324
11336
  }
11325
11337
  result = await triggerTaskPlannerViaCodex(reason, details, {
@@ -11355,7 +11367,7 @@ async function triggerTaskPlanner(
11355
11367
  );
11356
11368
  if (notify) {
11357
11369
  await sendTelegramMessage(
11358
- `⚠️ Task planner codex path failed; trying kanban fallback.\nReason: ${codexMessage}`,
11370
+ `:alert: Task planner codex path failed; trying kanban fallback.\nReason: ${codexMessage}`,
11359
11371
  );
11360
11372
  }
11361
11373
 
@@ -11472,7 +11484,7 @@ async function triggerTaskPlannerViaKanban(
11472
11484
  if (notify) {
11473
11485
  const suffix = taskUrl ? `\n${taskUrl}` : "";
11474
11486
  await sendTelegramMessage(
11475
- `📋 Task planner skipped — existing planning task found.${suffix}`,
11487
+ `:clipboard: Task planner skipped — existing planning task found.${suffix}`,
11476
11488
  );
11477
11489
  }
11478
11490
  await updatePlannerState({
@@ -11534,7 +11546,7 @@ async function triggerTaskPlannerViaKanban(
11534
11546
  if (notify) {
11535
11547
  const suffix = createdUrl ? `\n${createdUrl}` : "";
11536
11548
  await sendTelegramMessage(
11537
- `📋 Task planner: created task for next phase planning (${reason}).${suffix}`,
11549
+ `:clipboard: Task planner: created task for next phase planning (${reason}).${suffix}`,
11538
11550
  );
11539
11551
  }
11540
11552
  return {
@@ -11662,6 +11674,12 @@ async function triggerTaskPlannerViaCodex(
11662
11674
  })),
11663
11675
  );
11664
11676
 
11677
+ if (created.length === 0) {
11678
+ throw new Error(
11679
+ `Task planner parsed ${parsedTasks.length} tasks but created 0 tasks after dedup/materialization`,
11680
+ );
11681
+ }
11682
+
11665
11683
  console.log(`[monitor] task planner output saved: ${outPath}`);
11666
11684
  console.log(
11667
11685
  `[monitor] task planner artifact saved: ${artifactPath} (parsed=${parsedTasks.length}, created=${created.length}, skipped=${skipped.length})`,
@@ -11674,7 +11692,7 @@ async function triggerTaskPlannerViaCodex(
11674
11692
  });
11675
11693
  if (notify) {
11676
11694
  await sendTelegramMessage(
11677
- `📋 Task planner run completed (${reason || "manual"}). Created ${created.length}/${parsedTasks.length} tasks.${
11695
+ `:clipboard: Task planner run completed (${reason || "manual"}). Created ${created.length}/${parsedTasks.length} tasks.${
11678
11696
  skipped.length > 0
11679
11697
  ? ` Skipped ${skipped.length} duplicates/failed.`
11680
11698
  : ""
@@ -11911,7 +11929,7 @@ ${logTail}
11911
11929
  if (telegramToken && telegramChatId) {
11912
11930
  const summary = analysisText.slice(0, 500).replace(/\n{3,}/g, "\n\n");
11913
11931
  void sendTelegramMessage(
11914
- `🔍 Codex Analysis Result (${reason}):\n${summary}${analysisText.length > 500 ? "\n...(truncated)" : ""}`,
11932
+ `:search: Codex Analysis Result (${reason}):\n${summary}${analysisText.length > 500 ? "\n...(truncated)" : ""}`,
11915
11933
  );
11916
11934
  }
11917
11935
  } catch (err) {
@@ -11930,7 +11948,7 @@ ${logTail}
11930
11948
  if (telegramToken && telegramChatId) {
11931
11949
  const summary = analysisText.slice(0, 500).replace(/\n{3,}/g, "\n\n");
11932
11950
  void sendTelegramMessage(
11933
- `🔍 Codex Analysis Result (${reason}):\n${summary}${analysisText.length > 500 ? "\n...(truncated)" : ""}`,
11951
+ `:search: Codex Analysis Result (${reason}):\n${summary}${analysisText.length > 500 ? "\n...(truncated)" : ""}`,
11934
11952
  );
11935
11953
  }
11936
11954
  } catch (fallbackErr) {
@@ -11942,7 +11960,7 @@ ${logTail}
11942
11960
  "utf8",
11943
11961
  );
11944
11962
  if (telegramToken && telegramChatId) {
11945
- void sendTelegramMessage(`🔍 Codex Analysis Failed: ${message}`);
11963
+ void sendTelegramMessage(`:search: Codex Analysis Failed: ${message}`);
11946
11964
  }
11947
11965
  }
11948
11966
  }
@@ -12069,7 +12087,7 @@ async function handleExit(code, signal, logPath) {
12069
12087
  );
12070
12088
  if (telegramToken && telegramChatId) {
12071
12089
  void sendTelegramMessage(
12072
- `⏳ Mutex held — backing off ${exitState.backoffMs / 1000}s before retry`,
12090
+ `:clock: Mutex held — backing off ${exitState.backoffMs / 1000}s before retry`,
12073
12091
  );
12074
12092
  }
12075
12093
  restartCount += 1;
@@ -12257,7 +12275,7 @@ async function handleExit(code, signal, logPath) {
12257
12275
  }
12258
12276
  if (telegramToken && telegramChatId) {
12259
12277
  void sendTelegramMessage(
12260
- `🛑 Crash loop detected (${restartCountNow} exits in 5m). Pausing orchestrator restarts for ${pauseMin} minutes. Background fix running.`,
12278
+ `:close: Crash loop detected (${restartCountNow} exits in 5m). Pausing orchestrator restarts for ${pauseMin} minutes. Background fix running.`,
12261
12279
  );
12262
12280
  }
12263
12281
  // ── Background crash-loop fix: runs while orchestrator is paused ──
@@ -12277,7 +12295,7 @@ async function handleExit(code, signal, logPath) {
12277
12295
  );
12278
12296
  if (telegramToken && telegramChatId) {
12279
12297
  void sendTelegramMessage(
12280
- `🛠️ Crash-loop fix applied. File watcher will restart orchestrator.\n${fixResult.outcome}`,
12298
+ `:u1f6e0: Crash-loop fix applied. File watcher will restart orchestrator.\n${fixResult.outcome}`,
12281
12299
  );
12282
12300
  }
12283
12301
  } else {
@@ -12291,11 +12309,11 @@ async function handleExit(code, signal, logPath) {
12291
12309
  );
12292
12310
  if (freshStarted && telegramToken && telegramChatId) {
12293
12311
  void sendTelegramMessage(
12294
- `🔄 Crash-loop fix failed but fresh session started. New agent will retry.`,
12312
+ `:refresh: Crash-loop fix failed but fresh session started. New agent will retry.`,
12295
12313
  );
12296
12314
  } else if (!freshStarted && telegramToken && telegramChatId) {
12297
12315
  void sendTelegramMessage(
12298
- `⚠️ Crash-loop fix failed: ${fixResult.outcome}. Orchestrator will resume after ${pauseMin}m pause.`,
12316
+ `:alert: Crash-loop fix failed: ${fixResult.outcome}. Orchestrator will resume after ${pauseMin}m pause.`,
12299
12317
  );
12300
12318
  }
12301
12319
  }
@@ -12606,7 +12624,7 @@ function buildMonitorMonitorStatusText(
12606
12624
  );
12607
12625
 
12608
12626
  const lines = [
12609
- "🛰️ Bosun-Monitor Update",
12627
+ ":server: Bosun-Monitor Update",
12610
12628
  `- Reason: ${reason}`,
12611
12629
  `- Running: ${monitorMonitor.running ? "yes" : "no"}`,
12612
12630
  `- Current SDK: ${currentSdk}`,
@@ -13420,7 +13438,7 @@ async function runMonitorMonitorCycle({
13420
13438
  rotateMonitorSdk("prepare next cycle");
13421
13439
  }
13422
13440
  void notify?.(
13423
- `⚠️ Monitor-Monitor failed (${sdk}): ${String(errMsg).slice(0, 240)}`,
13441
+ `:alert: Monitor-Monitor failed (${sdk}): ${String(errMsg).slice(0, 240)}`,
13424
13442
  3,
13425
13443
  { dedupKey: "monitor-monitor-failed" },
13426
13444
  );
@@ -13439,7 +13457,7 @@ async function runMonitorMonitorCycle({
13439
13457
  monitorMonitor.lastError = errMsg;
13440
13458
  console.error(`[monitor-monitor] uncaught exception via ${sdk}: ${errMsg}`);
13441
13459
  void notify?.(
13442
- `⚠️ Monitor-Monitor exception (${sdk}): ${errMsg.slice(0, 240)}`,
13460
+ `:alert: Monitor-Monitor exception (${sdk}): ${errMsg.slice(0, 240)}`,
13443
13461
  3,
13444
13462
  { dedupKey: "monitor-monitor-exception" },
13445
13463
  );
@@ -13733,7 +13751,7 @@ async function startProcess() {
13733
13751
  );
13734
13752
  if (telegramToken && telegramChatId) {
13735
13753
  void sendTelegramMessage(
13736
- `❌ Orchestrator script not found: ${scriptPath}\nSet ORCHESTRATOR_SCRIPT to a valid path.`,
13754
+ `:close: Orchestrator script not found: ${scriptPath}\nSet ORCHESTRATOR_SCRIPT to a valid path.`,
13737
13755
  );
13738
13756
  }
13739
13757
  return;
@@ -13766,7 +13784,7 @@ async function startProcess() {
13766
13784
  );
13767
13785
  if (telegramToken && telegramChatId) {
13768
13786
  void sendTelegramMessage(
13769
- `❌ .ps1 orchestrator selected, but PowerShell runtime is unavailable (${pwshLabel}).\n` +
13787
+ `:close: .ps1 orchestrator selected, but PowerShell runtime is unavailable (${pwshLabel}).\n` +
13770
13788
  `Install PowerShell 7+ or set PWSH_PATH to a valid executable path. ` +
13771
13789
  `Pausing restarts for ${pauseMin} minute(s).`,
13772
13790
  );
@@ -13795,7 +13813,7 @@ async function startProcess() {
13795
13813
  );
13796
13814
  if (telegramToken && telegramChatId) {
13797
13815
  void sendTelegramMessage(
13798
- " shell-mode orchestrator selected (.sh), but bash/sh is missing on PATH.",
13816
+ ":close: shell-mode orchestrator selected (.sh), but bash/sh is missing on PATH.",
13799
13817
  );
13800
13818
  }
13801
13819
  return;
@@ -14710,7 +14728,7 @@ async function reloadConfig(reason) {
14710
14728
  if (telegramToken && telegramChatId) {
14711
14729
  try {
14712
14730
  await sendTelegramMessage(
14713
- `🔄 .env reloaded (${reason}). Runtime config updated.`,
14731
+ `:refresh: .env reloaded (${reason}). Runtime config updated.`,
14714
14732
  { dedupKey: "env-reload" },
14715
14733
  );
14716
14734
  } catch {
@@ -15486,7 +15504,7 @@ try {
15486
15504
 
15487
15505
  if (isExecutorDisabled()) {
15488
15506
  console.log(
15489
- `[monitor] task execution DISABLED (EXECUTOR_MODE=${executorMode}) — no tasks will be executed`,
15507
+ `[monitor] :ban: task execution DISABLED (EXECUTOR_MODE=${executorMode}) — no tasks will be executed`,
15490
15508
  );
15491
15509
  } else if (executorMode === "internal" || executorMode === "hybrid") {
15492
15510
  // Start internal executor
@@ -15514,7 +15532,7 @@ if (isExecutorDisabled()) {
15514
15532
  : "n/a";
15515
15533
  const taskId = String(task?.id || task?.task_id || "").trim();
15516
15534
  console.log(
15517
- `[task-executor] 🚀 started: "${task.title}" (${slot.sdk}) agent=${agentId} branch=${slot.branch} worktree=${slot.worktreePath || "(pending)"}`,
15535
+ `[task-executor] :rocket: started: "${task.title}" (${slot.sdk}) agent=${agentId} branch=${slot.branch} worktree=${slot.worktreePath || "(pending)"}`,
15518
15536
  );
15519
15537
  if (agentEventBus) agentEventBus.onTaskStarted(task, slot);
15520
15538
  if (taskId) {
@@ -15574,8 +15592,8 @@ if (isExecutorDisabled()) {
15574
15592
  ).trim() || null;
15575
15593
  console.log(
15576
15594
  finalizationFailed
15577
- ? `[task-executor] completed without finalization: "${task.title}" (${result.attempts} attempt(s), reason=${result?.finalizationReason || "unknown"})`
15578
- : `[task-executor] completed: "${task.title}" (${result.attempts} attempt(s))`,
15595
+ ? `[task-executor] :alert: completed without finalization: "${task.title}" (${result.attempts} attempt(s), reason=${result?.finalizationReason || "unknown"})`
15596
+ : `[task-executor] :check: completed: "${task.title}" (${result.attempts} attempt(s))`,
15579
15597
  );
15580
15598
  if (!finalizationFailed && agentEventBus) {
15581
15599
  agentEventBus.onTaskCompleted(task, result);
@@ -15660,7 +15678,7 @@ if (isExecutorDisabled()) {
15660
15678
  const attempts =
15661
15679
  Number(err?.attempts || 0) > 0 ? Number(err.attempts) : null;
15662
15680
  console.warn(
15663
- `[task-executor] failed: "${task.title}" — ${formatMonitorError(err)}`,
15681
+ `[task-executor] :close: failed: "${task.title}" — ${formatMonitorError(err)}`,
15664
15682
  );
15665
15683
  if (agentEventBus) agentEventBus.onTaskFailed(task, err);
15666
15684
  if (taskId) {
@@ -16074,7 +16092,7 @@ if (isExecutorDisabled()) {
16074
16092
  telegramToken && telegramChatId
16075
16093
  ? (event) =>
16076
16094
  sendTelegramMessage(
16077
- `⚠️ Project sync alert: ${event?.message || "unknown"}`,
16095
+ `:alert: Project sync alert: ${event?.message || "unknown"}`,
16078
16096
  )
16079
16097
  : null,
16080
16098
  failureAlertThreshold:
@@ -16132,7 +16150,7 @@ startMonitorMonitorSupervisor();
16132
16150
  startTaskPlannerStatusLoop();
16133
16151
  restartGitHubReconciler();
16134
16152
 
16135
- // ── Two-way Telegram primary agent ────────────────────────────────────────
16153
+ // ── Two-way Telegram :workflow: primary agent ────────────────────────────────────────
16136
16154
  injectMonitorFunctions({
16137
16155
  sendTelegramMessage,
16138
16156
  readStatusData,