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/ui/demo.html CHANGED
@@ -874,7 +874,7 @@
874
874
  { id: 'do-prompt', type: 'action.run_agent', label: 'Prompt Agent', config: { prompt: 'Continue working. Instructions: {{decision.message}}' }, position: { x: 300, y: 680 } },
875
875
  { id: 'do-close', type: 'action.run_command', label: 'Close PR', config: { command: 'gh pr close --comment "{{decision.reason}}"' }, position: { x: 500, y: 680 } },
876
876
  { id: 'do-retry', type: 'action.run_agent', label: 'Re-attempt Task', config: { prompt: 'Start over: {{decision.reason}}' }, position: { x: 700, y: 680 } },
877
- { id: 'do-escalate', type: 'notify.telegram', label: 'Escalate to Human', config: { message: 'πŸ‘€ PR needs manual review: {{decision.reason}}' }, position: { x: 900, y: 680 } },
877
+ { id: 'do-escalate', type: 'notify.telegram', label: 'Escalate to Human', config: { message: ':eye: PR needs manual review: {{decision.reason}}' }, position: { x: 900, y: 680 } },
878
878
  { id: 'notify-complete', type: 'notify.log', label: 'Log Result', config: { message: 'PR merge strategy: {{decision.action}}', level: 'info' }, position: { x: 400, y: 850 } },
879
879
  ],
880
880
  edges: [
@@ -916,7 +916,7 @@
916
916
  { id: 'run-tests', type: 'validation.tests', label: 'Run Tests', config: { command: 'npm test' }, position: { x: 600, y: 200 } },
917
917
  { id: 'run-review',type: 'action.run_agent', label: 'Agent Review', config: { prompt: 'Review changes for quality, bugs, test coverage, and docs.', timeoutMs: 900000 }, position: { x: 400, y: 350 } },
918
918
  { id: 'aggregate', type: 'transform.aggregate', label: 'Collect Results', config: { sources: ['run-build', 'run-tests', 'run-review'] }, position: { x: 400, y: 500 } },
919
- { id: 'notify', type: 'notify.telegram', label: 'Post Review', config: { message: 'πŸ“ PR review complete for {{branch}}' }, position: { x: 400, y: 640 } },
919
+ { id: 'notify', type: 'notify.telegram', label: 'Post Review', config: { message: ':edit: PR review complete for {{branch}}' }, position: { x: 400, y: 640 } },
920
920
  ],
921
921
  edges: [
922
922
  { id: 'e1', source: 'trigger', target: 'run-build' },
@@ -943,7 +943,7 @@
943
943
  { id: 'check-retries', type: 'condition.expression', label: 'Retries Left?', config: { expression: '($data?.retryCount || 0) < ($data?.maxRetries || 3)' }, position: { x: 400, y: 180 } },
944
944
  { id: 'analyze-error', type: 'action.run_agent', label: 'Analyze Failure', config: { prompt: 'Analyze this error and suggest a fix:\n{{lastError}}\nTask: {{taskTitle}}', timeoutMs: 300000 }, position: { x: 200, y: 330 } },
945
945
  { id: 'retry-task', type: 'action.run_agent', label: 'Retry Task', config: { prompt: '{{taskExecutorRetryPrompt}}', timeoutMs: 3600000 }, position: { x: 200, y: 480 } },
946
- { id: 'escalate', type: 'notify.telegram', label: 'Escalate to Human', config: { message: '🚨 Task {{taskTitle}} failed after {{maxRetries}} attempts. Manual intervention needed.' }, position: { x: 600, y: 180 } },
946
+ { id: 'escalate', type: 'notify.telegram', label: 'Escalate to Human', config: { message: ':alert: Task {{taskTitle}} failed after {{maxRetries}} attempts. Manual intervention needed.' }, position: { x: 600, y: 180 } },
947
947
  ],
948
948
  edges: [
949
949
  { id: 'e1', source: 'trigger', target: 'check-retries' },
@@ -969,7 +969,7 @@
969
969
  { id: 'test', type: 'validation.tests', label: 'Tests', config: { command: 'npm test' }, position: { x: 400, y: 310 } },
970
970
  { id: 'lint', type: 'validation.lint', label: 'Lint', config: { command: 'npm run lint' }, position: { x: 400, y: 440 } },
971
971
  { id: 'deploy', type: 'action.run_command', label: 'Deploy', config: { command: 'npm run deploy' }, position: { x: 400, y: 570 } },
972
- { id: 'notify', type: 'notify.telegram', label: 'Notify Deploy', config: { message: 'πŸš€ Deployment to production completed for {{branch}}' }, position: { x: 400, y: 700 } },
972
+ { id: 'notify', type: 'notify.telegram', label: 'Notify Deploy', config: { message: ':rocket: Deployment to production completed for {{branch}}' }, position: { x: 400, y: 700 } },
973
973
  ],
974
974
  edges: [
975
975
  { id: 'e1', source: 'trigger', target: 'build' },
@@ -996,7 +996,7 @@
996
996
  { id: 'check-low', type: 'condition.expression', label: 'Tasks Low?', config: { expression: '$data?.count < 3' }, position: { x: 400, y: 310 } },
997
997
  { id: 'plan-tasks', type: 'action.run_agent', label: 'Plan New Tasks', config: { prompt: 'Review the codebase and create 3-5 actionable improvement tasks.', timeoutMs: 600000 }, position: { x: 200, y: 460 } },
998
998
  { id: 'create-tasks',type: 'action.run_command', label: 'Create Tasks', config: { command: 'bosun task create --from-plan' }, position: { x: 200, y: 610 } },
999
- { id: 'notify', type: 'notify.telegram', label: 'Notify Planner Done',config: { message: 'πŸ“‹ Task planner added new tasks to backlog' }, position: { x: 200, y: 760 } },
999
+ { id: 'notify', type: 'notify.telegram', label: 'Notify Planner Done',config: { message: ':clipboard: Task planner added new tasks to backlog' }, position: { x: 200, y: 760 } },
1000
1000
  { id: 'skip', type: 'notify.log', label: 'Backlog OK', config: { message: 'Backlog has enough tasks, skipping', level: 'debug' }, position: { x: 600, y: 460 } },
1001
1001
  ],
1002
1002
  edges: [
@@ -1027,7 +1027,7 @@
1027
1027
  { id: 'screenshot', type: 'action.run_command', label: 'Take Screenshots', config: { command: 'bosun screenshot --viewport desktop,mobile' }, position: { x: 400, y: 500 } },
1028
1028
  { id: 'model-review', type: 'action.run_agent', label: 'Model Verification', config: { prompt: 'Verify screenshots match task requirements. Pass/fail.', timeoutMs: 300000 }, position: { x: 400, y: 650 } },
1029
1029
  { id: 'mark-done', type: 'action.update_task_status', label: 'Mark Done', config: { status: 'done' }, position: { x: 200, y: 800 } },
1030
- { id: 'notify-fail', type: 'notify.telegram', label: 'Notify Failed', config: { message: '❌ Frontend verification failed for {{taskTitle}}' }, position: { x: 600, y: 800 } },
1030
+ { id: 'notify-fail', type: 'notify.telegram', label: 'Notify Failed', config: { message: ':close: Frontend verification failed for {{taskTitle}}' }, position: { x: 600, y: 800 } },
1031
1031
  ],
1032
1032
  edges: [
1033
1033
  { id: 'e1', source: 'trigger', target: 'run-agent' },
@@ -1045,7 +1045,7 @@
1045
1045
  ],
1046
1046
  workflowTemplates: [
1047
1047
  {
1048
- id: 'template-pr-merge-strategy', name: 'PR Merge Strategy', description: 'Automated PR merge decision workflow with 7 outcome paths.', category: 'github', categoryLabel: 'GitHub', categoryIcon: 'πŸ™', categoryOrder: 1, tags: ['github', 'pr', 'merge', 'strategy'], nodeCount: 14, edgeCount: 16, recommended: true,
1048
+ id: 'template-pr-merge-strategy', name: 'PR Merge Strategy', description: 'Automated PR merge decision workflow with 7 outcome paths.', category: 'github', categoryLabel: 'GitHub', categoryIcon: ':git:', categoryOrder: 1, tags: ['github', 'pr', 'merge', 'strategy'], nodeCount: 14, edgeCount: 16, recommended: true,
1049
1049
  nodes: [
1050
1050
  { id: 'trigger', type: 'trigger.pr_event', label: 'PR Ready for Review', config: { event: 'review_requested' }, position: { x: 400, y: 50 } },
1051
1051
  { id: 'check-ci', type: 'validation.build', label: 'Check CI Status', config: { command: 'gh pr checks' }, position: { x: 150, y: 200 } },
@@ -1058,7 +1058,7 @@
1058
1058
  { id: 'do-prompt', type: 'action.run_agent', label: 'Prompt Agent', config: { prompt: 'Address the reviewer feedback on this PR. Fix the issues identified and push updated commits.', sdk: 'auto' }, position: { x: 300, y: 680 } },
1059
1059
  { id: 'do-close', type: 'action.run_command', label: 'Close PR', config: { command: 'gh pr close' }, position: { x: 500, y: 680 } },
1060
1060
  { id: 'do-retry', type: 'action.run_agent', label: 'Re-attempt', config: { prompt: 'Re-attempt the implementation from scratch. The previous approach had issues. Start fresh on a new branch.', sdk: 'auto' }, position: { x: 700, y: 680 } },
1061
- { id: 'do-escalate', type: 'notify.telegram', label: 'Escalate', config: { message: '🚨 PR needs manual review β€” auto-merge could not decide. Please review and merge manually.' }, position: { x: 900, y: 680 } },
1061
+ { id: 'do-escalate', type: 'notify.telegram', label: 'Escalate', config: { message: ':alert: PR needs manual review β€” auto-merge could not decide. Please review and merge manually.' }, position: { x: 900, y: 680 } },
1062
1062
  { id: 'notify-complete', type: 'notify.log', label: 'Log Result', config: { message: 'PR merge decision workflow completed', level: 'info' }, position: { x: 400, y: 850 } },
1063
1063
  ],
1064
1064
  edges: [
@@ -1075,7 +1075,7 @@
1075
1075
  ],
1076
1076
  },
1077
1077
  {
1078
- id: 'template-pr-triage', name: 'PR Triage & Labels', description: 'Auto-classify PRs by size, detect breaking changes, assign labels.', category: 'github', categoryLabel: 'GitHub', categoryIcon: 'πŸ™', categoryOrder: 1, tags: ['github', 'pr', 'triage', 'labels'], nodeCount: 11, edgeCount: 12,
1078
+ id: 'template-pr-triage', name: 'PR Triage & Labels', description: 'Auto-classify PRs by size, detect breaking changes, assign labels.', category: 'github', categoryLabel: 'GitHub', categoryIcon: ':git:', categoryOrder: 1, tags: ['github', 'pr', 'triage', 'labels'], nodeCount: 11, edgeCount: 12,
1079
1079
  nodes: [
1080
1080
  { id: 'trigger', type: 'trigger.pr_event', label: 'PR Opened', config: { event: 'opened' }, position: { x: 400, y: 50 } },
1081
1081
  { id: 'get-stats', type: 'action.run_command', label: 'Get PR Stats', config: { command: 'gh pr view --json additions,deletions' }, position: { x: 400, y: 180 } },
@@ -1099,14 +1099,14 @@
1099
1099
  ],
1100
1100
  },
1101
1101
  {
1102
- id: 'template-review-agent', name: 'Review Agent', description: 'Automated PR code review: build, test, agent analysis.', category: 'github', categoryLabel: 'GitHub', categoryIcon: 'πŸ™', categoryOrder: 1, tags: ['github', 'review', 'pr'], nodeCount: 6, edgeCount: 6, recommended: true,
1102
+ id: 'template-review-agent', name: 'Review Agent', description: 'Automated PR code review: build, test, agent analysis.', category: 'github', categoryLabel: 'GitHub', categoryIcon: ':git:', categoryOrder: 1, tags: ['github', 'review', 'pr'], nodeCount: 6, edgeCount: 6, recommended: true,
1103
1103
  nodes: [
1104
1104
  { id: 'trigger', type: 'trigger.pr_event', label: 'PR Opened', config: { event: 'opened' }, position: { x: 400, y: 50 } },
1105
1105
  { id: 'run-build', type: 'validation.build', label: 'Run Build', config: { command: 'npm run build' }, position: { x: 200, y: 200 } },
1106
1106
  { id: 'run-tests', type: 'validation.tests', label: 'Run Tests', config: { command: 'npm test' }, position: { x: 600, y: 200 } },
1107
1107
  { id: 'run-review', type: 'action.run_agent', label: 'Agent Review', config: { prompt: 'Review for quality, bugs, coverage, docs.' }, position: { x: 400, y: 350 } },
1108
1108
  { id: 'aggregate', type: 'transform.aggregate', label: 'Collect Results', config: { sourceNodes: 'run-build,run-tests,run-review', outputField: 'reviewResults' }, position: { x: 400, y: 500 } },
1109
- { id: 'notify', type: 'notify.telegram', label: 'Post Review', config: { message: 'πŸ“ PR review complete' }, position: { x: 400, y: 640 } },
1109
+ { id: 'notify', type: 'notify.telegram', label: 'Post Review', config: { message: ':edit: PR review complete' }, position: { x: 400, y: 640 } },
1110
1110
  ],
1111
1111
  edges: [
1112
1112
  { id: 'e1', source: 'trigger', target: 'run-build' }, { id: 'e2', source: 'trigger', target: 'run-tests' },
@@ -1115,7 +1115,7 @@
1115
1115
  ],
1116
1116
  },
1117
1117
  {
1118
- id: 'template-frontend-agent', name: 'Frontend Agent', description: 'Front-end dev agent with screenshot validation.', category: 'agents', categoryLabel: 'Agents', categoryIcon: 'πŸ€–', categoryOrder: 2, tags: ['agent', 'frontend', 'screenshot'], nodeCount: 8, edgeCount: 9,
1118
+ id: 'template-frontend-agent', name: 'Frontend Agent', description: 'Front-end dev agent with screenshot validation.', category: 'agents', categoryLabel: 'Agents', categoryIcon: ':bot:', categoryOrder: 2, tags: ['agent', 'frontend', 'screenshot'], nodeCount: 8, edgeCount: 9,
1119
1119
  nodes: [
1120
1120
  { id: 'trigger', type: 'trigger.task_assigned', label: 'Task Assigned', config: { agentType: 'frontend' }, position: { x: 400, y: 50 } },
1121
1121
  { id: 'run-agent', type: 'action.run_agent', label: 'Run Frontend Agent', config: { prompt: 'Implement: {{taskTitle}}' }, position: { x: 400, y: 200 } },
@@ -1124,7 +1124,7 @@
1124
1124
  { id: 'screenshot', type: 'action.run_command', label: 'Screenshots', config: { command: 'bosun screenshot' }, position: { x: 400, y: 500 } },
1125
1125
  { id: 'model-review', type: 'action.run_agent', label: 'Model Review', config: { prompt: 'Verify screenshots match requirements' }, position: { x: 400, y: 650 } },
1126
1126
  { id: 'mark-done', type: 'action.update_task_status', label: 'Mark Done', config: { status: 'done' }, position: { x: 200, y: 800 } },
1127
- { id: 'notify-fail', type: 'notify.telegram', label: 'Notify Failed', config: { message: '❌ Verification failed' }, position: { x: 600, y: 800 } },
1127
+ { id: 'notify-fail', type: 'notify.telegram', label: 'Notify Failed', config: { message: ':close: Verification failed' }, position: { x: 600, y: 800 } },
1128
1128
  ],
1129
1129
  edges: [
1130
1130
  { id: 'e1', source: 'trigger', target: 'run-agent' }, { id: 'e2', source: 'run-agent', target: 'verify-build' },
@@ -1135,14 +1135,14 @@
1135
1135
  ],
1136
1136
  },
1137
1137
  {
1138
- id: 'template-task-planner', name: 'Task Planner', description: 'Auto-replenish backlog when tasks run low.', category: 'planning', categoryLabel: 'Planning', categoryIcon: 'πŸ“‹', categoryOrder: 3, tags: ['planning', 'backlog'], nodeCount: 7, edgeCount: 7, recommended: true,
1138
+ id: 'template-task-planner', name: 'Task Planner', description: 'Auto-replenish backlog when tasks run low.', category: 'planning', categoryLabel: 'Planning', categoryIcon: ':clipboard:', categoryOrder: 3, tags: ['planning', 'backlog'], nodeCount: 7, edgeCount: 7, recommended: true,
1139
1139
  nodes: [
1140
1140
  { id: 'trigger', type: 'trigger.cron', label: 'Every 30 min', config: { cron: '*/30 * * * *' }, position: { x: 400, y: 50 } },
1141
1141
  { id: 'count-tasks', type: 'action.run_command', label: 'Count Tasks', config: { command: 'bosun task list --status todo --count' }, position: { x: 400, y: 180 } },
1142
1142
  { id: 'check-low', type: 'condition.expression', label: 'Tasks Low?', config: { expression: '$data?.count < 3' }, position: { x: 400, y: 310 } },
1143
1143
  { id: 'plan-tasks', type: 'action.run_agent', label: 'Plan New Tasks', config: { prompt: 'Create 3-5 improvement tasks' }, position: { x: 200, y: 460 } },
1144
1144
  { id: 'create-tasks', type: 'action.run_command', label: 'Create Tasks', config: { command: 'bosun task import --from-plan', timeoutMs: 60000 }, position: { x: 200, y: 610 } },
1145
- { id: 'notify', type: 'notify.telegram', label: 'Notify Done', config: { message: 'πŸ“‹ Tasks replenished' }, position: { x: 200, y: 760 } },
1145
+ { id: 'notify', type: 'notify.telegram', label: 'Notify Done', config: { message: ':clipboard: Tasks replenished' }, position: { x: 200, y: 760 } },
1146
1146
  { id: 'skip', type: 'notify.log', label: 'Backlog OK', config: { message: 'Backlog has enough tasks β€” skipping replenishment', level: 'info' }, position: { x: 600, y: 460 } },
1147
1147
  ],
1148
1148
  edges: [
@@ -1152,14 +1152,14 @@
1152
1152
  ],
1153
1153
  },
1154
1154
  {
1155
- id: 'template-task-replenish', name: 'Task Replenish', description: 'Periodic task replenishment on cron schedule.', category: 'planning', categoryLabel: 'Planning', categoryIcon: 'πŸ“‹', categoryOrder: 3, tags: ['planning', 'cron'], nodeCount: 7, edgeCount: 6,
1155
+ id: 'template-task-replenish', name: 'Task Replenish', description: 'Periodic task replenishment on cron schedule.', category: 'planning', categoryLabel: 'Planning', categoryIcon: ':clipboard:', categoryOrder: 3, tags: ['planning', 'cron'], nodeCount: 7, edgeCount: 6,
1156
1156
  nodes: [
1157
1157
  { id: 'trigger', type: 'trigger.cron', label: 'Cron Schedule', config: { cron: '0 */6 * * *' }, position: { x: 400, y: 50 } },
1158
1158
  { id: 'scan', type: 'action.run_command', label: 'Scan Backlog', config: { command: 'bosun task list --status todo --json', timeoutMs: 30000 }, position: { x: 400, y: 180 } },
1159
1159
  { id: 'analyze', type: 'action.run_agent', label: 'Analyze Gaps', config: { prompt: 'Find missing tasks' }, position: { x: 400, y: 310 } },
1160
1160
  { id: 'create', type: 'action.run_command', label: 'Create Tasks', config: { command: 'bosun task import --from-plan', timeoutMs: 60000 }, position: { x: 400, y: 440 } },
1161
1161
  { id: 'prioritize', type: 'action.run_agent', label: 'Prioritize', config: { prompt: 'Prioritize the newly created tasks by impact and effort. Assign priorities 1-10.', sdk: 'auto' }, position: { x: 400, y: 570 } },
1162
- { id: 'notify', type: 'notify.telegram', label: 'Notify', config: { message: 'πŸ“‹ Backlog replenished with new tasks' }, position: { x: 400, y: 700 } },
1162
+ { id: 'notify', type: 'notify.telegram', label: 'Notify', config: { message: ':clipboard: Backlog replenished with new tasks' }, position: { x: 400, y: 700 } },
1163
1163
  ],
1164
1164
  edges: [
1165
1165
  { id: 'e1', source: 'trigger', target: 'scan' }, { id: 'e2', source: 'scan', target: 'analyze' },
@@ -1168,14 +1168,14 @@
1168
1168
  ],
1169
1169
  },
1170
1170
  {
1171
- id: 'template-build-deploy', name: 'Build & Deploy', description: 'CI/CD pipeline: build β†’ test β†’ lint β†’ deploy.', category: 'ci-cd', categoryLabel: 'CI / CD', categoryIcon: 'πŸ”„', categoryOrder: 4, tags: ['ci', 'cd', 'deploy'], nodeCount: 6, edgeCount: 5,
1171
+ id: 'template-build-deploy', name: 'Build & Deploy', description: 'CI/CD pipeline: build β†’ test β†’ lint β†’ deploy.', category: 'ci-cd', categoryLabel: 'CI / CD', categoryIcon: ':refresh:', categoryOrder: 4, tags: ['ci', 'cd', 'deploy'], nodeCount: 6, edgeCount: 5,
1172
1172
  nodes: [
1173
1173
  { id: 'trigger', type: 'trigger.event', label: 'PR Merged', config: { eventType: 'pr.merged' }, position: { x: 400, y: 50 } },
1174
1174
  { id: 'build', type: 'validation.build', label: 'Build', config: { command: 'npm run build' }, position: { x: 400, y: 180 } },
1175
1175
  { id: 'test', type: 'validation.tests', label: 'Tests', config: { command: 'npm test' }, position: { x: 400, y: 310 } },
1176
1176
  { id: 'lint', type: 'validation.lint', label: 'Lint', config: { command: 'npm run lint' }, position: { x: 400, y: 440 } },
1177
1177
  { id: 'deploy', type: 'action.run_command', label: 'Deploy', config: { command: 'npm run deploy' }, position: { x: 400, y: 570 } },
1178
- { id: 'notify', type: 'notify.telegram', label: 'Notify', config: { message: 'πŸš€ Deployed' }, position: { x: 400, y: 700 } },
1178
+ { id: 'notify', type: 'notify.telegram', label: 'Notify', config: { message: ':rocket: Deployed' }, position: { x: 400, y: 700 } },
1179
1179
  ],
1180
1180
  edges: [
1181
1181
  { id: 'e1', source: 'trigger', target: 'build' }, { id: 'e2', source: 'build', target: 'test' },
@@ -1184,13 +1184,13 @@
1184
1184
  ],
1185
1185
  },
1186
1186
  {
1187
- id: 'template-error-recovery', name: 'Error Recovery', description: 'Auto-fix after agent crash or failure.', category: 'reliability', categoryLabel: 'Reliability', categoryIcon: 'πŸ›‘οΈ', categoryOrder: 5, tags: ['error', 'recovery', 'autofix'], nodeCount: 5, edgeCount: 4, recommended: true,
1187
+ id: 'template-error-recovery', name: 'Error Recovery', description: 'Auto-fix after agent crash or failure.', category: 'reliability', categoryLabel: 'Reliability', categoryIcon: ':shield:', categoryOrder: 5, tags: ['error', 'recovery', 'autofix'], nodeCount: 5, edgeCount: 4, recommended: true,
1188
1188
  nodes: [
1189
1189
  { id: 'trigger', type: 'trigger.event', label: 'Agent Failed', config: { eventType: 'task.failed' }, position: { x: 400, y: 50 } },
1190
1190
  { id: 'check-retries', type: 'condition.expression', label: 'Retries Left?', config: { expression: '($data?.retryCount || 0) < 3', trueLabel: 'retries > 0', falseLabel: 'exhausted' }, position: { x: 400, y: 180 } },
1191
1191
  { id: 'analyze-error', type: 'action.run_agent', label: 'Analyze Failure', config: { prompt: 'Analyze the failure: {{lastError}}\nDetermine root cause and suggest a targeted fix.', sdk: 'auto' }, position: { x: 200, y: 330 } },
1192
1192
  { id: 'retry-task', type: 'action.run_agent', label: 'Retry Task', config: { prompt: 'Retry the failed task. Previous error: {{lastError}}\nApply the fix from analysis and complete the task.', sdk: 'auto' }, position: { x: 200, y: 480 } },
1193
- { id: 'escalate', type: 'notify.telegram', label: 'Escalate', config: { message: '🚨 Agent failed after max retries β€” manual intervention needed. Task: {{taskTitle}}' }, position: { x: 600, y: 180 } },
1193
+ { id: 'escalate', type: 'notify.telegram', label: 'Escalate', config: { message: ':alert: Agent failed after max retries β€” manual intervention needed. Task: {{taskTitle}}' }, position: { x: 600, y: 180 } },
1194
1194
  ],
1195
1195
  edges: [
1196
1196
  { id: 'e1', source: 'trigger', target: 'check-retries' },
@@ -1200,7 +1200,7 @@
1200
1200
  ],
1201
1201
  },
1202
1202
  {
1203
- id: 'template-custom-agent', name: 'Custom Agent', description: 'Starter template for creating custom agent profiles.', category: 'agents', categoryLabel: 'Agents', categoryIcon: 'πŸ€–', categoryOrder: 2, tags: ['custom', 'agent', 'starter'], nodeCount: 6, edgeCount: 6,
1203
+ id: 'template-custom-agent', name: 'Custom Agent', description: 'Starter template for creating custom agent profiles.', category: 'agents', categoryLabel: 'Agents', categoryIcon: ':bot:', categoryOrder: 2, tags: ['custom', 'agent', 'starter'], nodeCount: 6, edgeCount: 6,
1204
1204
  nodes: [
1205
1205
  { id: 'trigger', type: 'trigger.manual', label: 'Start', config: { inputFields: 'taskId,prompt' }, position: { x: 400, y: 50 } },
1206
1206
  { id: 'set-context', type: 'action.run_command', label: 'Set Context', config: { command: 'git status --porcelain && bosun task get {{taskId}} --json', timeoutMs: 30000 }, position: { x: 400, y: 180 } },
@@ -1219,7 +1219,7 @@
1219
1219
  },
1220
1220
  // ── New Templates ──────────────────────────────────────────
1221
1221
  {
1222
- id: 'template-anomaly-watchdog', name: 'Anomaly Watchdog', description: 'Real-time anomaly detection β€” catches death loops, stalls, token overflows, rebase spirals.', category: 'reliability', categoryLabel: 'Reliability', categoryIcon: 'πŸ›‘οΈ', categoryOrder: 5, tags: ['anomaly', 'watchdog', 'death-loop', 'reliability'], nodeCount: 9, edgeCount: 12, recommended: true,
1222
+ id: 'template-anomaly-watchdog', name: 'Anomaly Watchdog', description: 'Real-time anomaly detection β€” catches death loops, stalls, token overflows, rebase spirals.', category: 'reliability', categoryLabel: 'Reliability', categoryIcon: ':shield:', categoryOrder: 5, tags: ['anomaly', 'watchdog', 'death-loop', 'reliability'], nodeCount: 9, edgeCount: 12, recommended: true,
1223
1223
  nodes: [
1224
1224
  { id: 'trigger', type: 'trigger.event', label: 'Anomaly Detected', config: { eventType: 'agent.anomaly' }, position: { x: 400, y: 50 } },
1225
1225
  { id: 'classify', type: 'condition.switch', label: 'Classify Anomaly', config: { field: 'anomalyType' }, position: { x: 400, y: 200 } },
@@ -1229,7 +1229,7 @@
1229
1229
  { id: 'fix-rebase', type: 'action.run_command', label: 'Fix Rebase', config: { command: 'git rebase --abort' }, position: { x: 700, y: 380 } },
1230
1230
  { id: 'break-spin', type: 'action.continue_session', label: 'Break Spin', config: { strategy: 'continue' }, position: { x: 900, y: 380 } },
1231
1231
  { id: 'log-intervention', type: 'notify.log', label: 'Log Intervention', config: { level: 'warn' }, position: { x: 400, y: 550 } },
1232
- { id: 'alert-telegram', type: 'notify.telegram', label: 'Alert Human', config: { message: '⚠️ Agent anomaly auto-resolved' }, position: { x: 400, y: 700 } },
1232
+ { id: 'alert-telegram', type: 'notify.telegram', label: 'Alert Human', config: { message: ':alert: Agent anomaly auto-resolved' }, position: { x: 400, y: 700 } },
1233
1233
  ],
1234
1234
  edges: [
1235
1235
  { id: 'e1', source: 'trigger', target: 'classify' },
@@ -1247,7 +1247,7 @@
1247
1247
  ],
1248
1248
  },
1249
1249
  {
1250
- id: 'template-workspace-hygiene', name: 'Workspace Hygiene', description: 'Scheduled maintenance: prune worktrees, kill stale processes, rotate logs.', category: 'reliability', categoryLabel: 'Reliability', categoryIcon: 'πŸ›‘οΈ', categoryOrder: 5, tags: ['maintenance', 'cleanup', 'worktree', 'hygiene'], nodeCount: 8, edgeCount: 9, recommended: true,
1250
+ id: 'template-workspace-hygiene', name: 'Workspace Hygiene', description: 'Scheduled maintenance: prune worktrees, kill stale processes, rotate logs.', category: 'reliability', categoryLabel: 'Reliability', categoryIcon: ':shield:', categoryOrder: 5, tags: ['maintenance', 'cleanup', 'worktree', 'hygiene'], nodeCount: 8, edgeCount: 9, recommended: true,
1251
1251
  nodes: [
1252
1252
  { id: 'trigger', type: 'trigger.schedule', label: 'Daily Cleanup', config: { cron: '0 3 * * *' }, position: { x: 400, y: 50 } },
1253
1253
  { id: 'prune-worktrees', type: 'action.run_command', label: 'Prune Worktrees', config: { command: 'git worktree prune' }, position: { x: 150, y: 200 } },
@@ -1267,7 +1267,7 @@
1267
1267
  ],
1268
1268
  },
1269
1269
  {
1270
- id: 'template-pr-conflict-resolver', name: 'PR Conflict Resolver', description: 'Auto-detect conflicted PRs, rebase, fix conflicts, re-run CI, merge.', category: 'github', categoryLabel: 'GitHub', categoryIcon: 'πŸ™', categoryOrder: 1, tags: ['github', 'pr', 'conflict', 'rebase'], nodeCount: 10, edgeCount: 9, recommended: true,
1270
+ id: 'template-pr-conflict-resolver', name: 'PR Conflict Resolver', description: 'Auto-detect conflicted PRs, rebase, fix conflicts, re-run CI, merge.', category: 'github', categoryLabel: 'GitHub', categoryIcon: ':git:', categoryOrder: 1, tags: ['github', 'pr', 'conflict', 'rebase'], nodeCount: 10, edgeCount: 9, recommended: true,
1271
1271
  nodes: [
1272
1272
  { id: 'trigger', type: 'trigger.schedule', label: 'Check Every 30min', config: { cron: '*/30 * * * *' }, position: { x: 400, y: 50 } },
1273
1273
  { id: 'list-prs', type: 'action.run_command', label: 'List Open PRs', config: { command: 'gh pr list --json number,mergeable --limit 20' }, position: { x: 400, y: 180 } },
@@ -1276,7 +1276,7 @@
1276
1276
  { id: 'verify-ci', type: 'action.run_command', label: 'Verify CI', config: { command: 'gh pr checks' }, position: { x: 200, y: 660 } },
1277
1277
  { id: 'auto-merge', type: 'condition.expression', label: 'CI Passed?', config: { expression: 'success === true' }, position: { x: 200, y: 810 } },
1278
1278
  { id: 'do-merge', type: 'action.run_command', label: 'Auto-Merge', config: { command: 'gh pr merge --auto --squash' }, position: { x: 100, y: 960 } },
1279
- { id: 'notify-fixed', type: 'notify.telegram', label: 'Notify Fixed', config: { message: 'πŸ”§ PR conflicts resolved and merged' }, position: { x: 100, y: 1100 } },
1279
+ { id: 'notify-fixed', type: 'notify.telegram', label: 'Notify Fixed', config: { message: ':settings: PR conflicts resolved and merged' }, position: { x: 100, y: 1100 } },
1280
1280
  { id: 'notify-failed', type: 'notify.log', label: 'CI Failed', config: { level: 'warn' }, position: { x: 400, y: 960 } },
1281
1281
  { id: 'skip', type: 'notify.log', label: 'No Conflicts', config: { level: 'info' }, position: { x: 600, y: 500 } },
1282
1282
  ],
@@ -1291,14 +1291,14 @@
1291
1291
  ],
1292
1292
  },
1293
1293
  {
1294
- id: 'template-health-check', name: 'Health Check', description: 'Periodic system health check: config, git state, agent status.', category: 'reliability', categoryLabel: 'Reliability', categoryIcon: 'πŸ›‘οΈ', categoryOrder: 5, tags: ['health', 'config', 'doctor', 'monitoring'], nodeCount: 7, edgeCount: 8,
1294
+ id: 'template-health-check', name: 'Health Check', description: 'Periodic system health check: config, git state, agent status.', category: 'reliability', categoryLabel: 'Reliability', categoryIcon: ':shield:', categoryOrder: 5, tags: ['health', 'config', 'doctor', 'monitoring'], nodeCount: 7, edgeCount: 8,
1295
1295
  nodes: [
1296
1296
  { id: 'trigger', type: 'trigger.schedule', label: 'Hourly Check', config: { cron: '0 * * * *' }, position: { x: 400, y: 50 } },
1297
1297
  { id: 'check-config', type: 'action.bosun_cli', label: 'Check Config', config: { subcommand: 'doctor' }, position: { x: 150, y: 200 } },
1298
1298
  { id: 'check-git', type: 'action.run_command', label: 'Check Git', config: { command: 'git status --porcelain' }, position: { x: 400, y: 200 } },
1299
1299
  { id: 'check-agents', type: 'action.run_command', label: 'Check Agents', config: { command: 'bosun --daemon-status' }, position: { x: 650, y: 200 } },
1300
1300
  { id: 'has-issues', type: 'condition.expression', label: 'Issues Found?', config: { expression: 'output.includes("ERROR")' }, position: { x: 400, y: 380 } },
1301
- { id: 'alert', type: 'notify.telegram', label: 'Alert Issues', config: { message: 'πŸ₯ Health check found issues' }, position: { x: 200, y: 540 } },
1301
+ { id: 'alert', type: 'notify.telegram', label: 'Alert Issues', config: { message: ':heart: Health check found issues' }, position: { x: 200, y: 540 } },
1302
1302
  { id: 'all-ok', type: 'notify.log', label: 'All Healthy', config: { level: 'info' }, position: { x: 600, y: 540 } },
1303
1303
  ],
1304
1304
  edges: [
@@ -1310,7 +1310,7 @@
1310
1310
  ],
1311
1311
  },
1312
1312
  {
1313
- id: 'template-stale-pr-reaper', name: 'Stale PR Reaper', description: 'Close stale PRs inactive for too long, clean up branches.', category: 'github', categoryLabel: 'GitHub', categoryIcon: 'πŸ™', categoryOrder: 1, tags: ['github', 'pr', 'stale', 'cleanup'], nodeCount: 8, edgeCount: 7,
1313
+ id: 'template-stale-pr-reaper', name: 'Stale PR Reaper', description: 'Close stale PRs inactive for too long, clean up branches.', category: 'github', categoryLabel: 'GitHub', categoryIcon: ':git:', categoryOrder: 1, tags: ['github', 'pr', 'stale', 'cleanup'], nodeCount: 8, edgeCount: 7,
1314
1314
  nodes: [
1315
1315
  { id: 'trigger', type: 'trigger.schedule', label: 'Daily Check', config: { cron: '0 8 * * *' }, position: { x: 400, y: 50 } },
1316
1316
  { id: 'find-stale', type: 'action.run_command', label: 'Find Stale PRs', config: { command: 'gh pr list --json number,updatedAt --limit 50' }, position: { x: 400, y: 200 } },
@@ -1318,7 +1318,7 @@
1318
1318
  { id: 'warn-stale', type: 'action.run_command', label: 'Post Warning', config: { command: 'echo "Would warn stale PRs"' }, position: { x: 200, y: 500 } },
1319
1319
  { id: 'close-stale', type: 'action.run_command', label: 'Close Expired', config: { command: 'echo "Would close stale PRs"' }, position: { x: 200, y: 650 } },
1320
1320
  { id: 'cleanup-branches', type: 'action.run_command', label: 'Prune Branches', config: { command: 'git fetch --prune origin' }, position: { x: 200, y: 800 } },
1321
- { id: 'summary', type: 'notify.telegram', label: 'Summary', config: { message: '🧹 Stale PR cleanup complete' }, position: { x: 200, y: 950 } },
1321
+ { id: 'summary', type: 'notify.telegram', label: 'Summary', config: { message: ':trash: Stale PR cleanup complete' }, position: { x: 200, y: 950 } },
1322
1322
  { id: 'skip', type: 'notify.log', label: 'No Stale PRs', config: { level: 'info' }, position: { x: 600, y: 500 } },
1323
1323
  ],
1324
1324
  edges: [
@@ -1330,7 +1330,7 @@
1330
1330
  ],
1331
1331
  },
1332
1332
  {
1333
- id: 'template-agent-session-monitor', name: 'Agent Session Monitor', description: 'Monitors active sessions for timeouts, inactivity, auto-continues stalled agents.', category: 'agents', categoryLabel: 'Agents', categoryIcon: 'πŸ€–', categoryOrder: 2, tags: ['agent', 'monitor', 'session', 'health'], nodeCount: 9, edgeCount: 8, recommended: true,
1333
+ id: 'template-agent-session-monitor', name: 'Agent Session Monitor', description: 'Monitors active sessions for timeouts, inactivity, auto-continues stalled agents.', category: 'agents', categoryLabel: 'Agents', categoryIcon: ':bot:', categoryOrder: 2, tags: ['agent', 'monitor', 'session', 'health'], nodeCount: 9, edgeCount: 8, recommended: true,
1334
1334
  nodes: [
1335
1335
  { id: 'trigger', type: 'trigger.schedule', label: 'Check Every 5min', config: { cron: '*/5 * * * *' }, position: { x: 400, y: 50 } },
1336
1336
  { id: 'list-sessions', type: 'action.run_command', label: 'List Sessions', config: { command: 'bosun agent list --json --active' }, position: { x: 400, y: 200 } },
@@ -1338,7 +1338,7 @@
1338
1338
  { id: 'check-health', type: 'action.run_command', label: 'Check Health', config: { command: 'bosun agent health --json' }, position: { x: 200, y: 490 } },
1339
1339
  { id: 'has-issues', type: 'condition.expression', label: 'Any Unhealthy?', config: { expression: 'output.includes("stalled")' }, position: { x: 200, y: 640 } },
1340
1340
  { id: 'auto-continue', type: 'action.continue_session', label: 'Auto-Continue', config: { strategy: 'continue' }, position: { x: 100, y: 800 } },
1341
- { id: 'alert-hung', type: 'notify.telegram', label: 'Alert Hung', config: { message: 'πŸ”΄ Agent session hung β€” auto-continue attempted' }, position: { x: 100, y: 950 } },
1341
+ { id: 'alert-hung', type: 'notify.telegram', label: 'Alert Hung', config: { message: ':dot: Agent session hung β€” auto-continue attempted' }, position: { x: 100, y: 950 } },
1342
1342
  { id: 'all-healthy', type: 'notify.log', label: 'All Healthy', config: { level: 'info' }, position: { x: 450, y: 640 } },
1343
1343
  { id: 'no-sessions', type: 'notify.log', label: 'No Sessions', config: { level: 'info' }, position: { x: 600, y: 490 } },
1344
1344
  ],
@@ -1353,14 +1353,14 @@
1353
1353
  ],
1354
1354
  },
1355
1355
  {
1356
- id: 'template-nightly-report', name: 'Nightly Report', description: 'Daily summary: tasks completed, PRs merged, errors, token usage.', category: 'planning', categoryLabel: 'Planning', categoryIcon: 'πŸ“‹', categoryOrder: 3, tags: ['report', 'daily', 'telegram', 'analytics'], nodeCount: 6, edgeCount: 7,
1356
+ id: 'template-nightly-report', name: 'Nightly Report', description: 'Daily summary: tasks completed, PRs merged, errors, token usage.', category: 'planning', categoryLabel: 'Planning', categoryIcon: ':clipboard:', categoryOrder: 3, tags: ['report', 'daily', 'telegram', 'analytics'], nodeCount: 6, edgeCount: 7,
1357
1357
  nodes: [
1358
1358
  { id: 'trigger', type: 'trigger.schedule', label: 'Nightly 11pm', config: { cron: '0 23 * * *' }, position: { x: 400, y: 50 } },
1359
1359
  { id: 'get-task-stats', type: 'action.run_command', label: 'Task Stats', config: { command: 'bosun task stats --json' }, position: { x: 150, y: 200 } },
1360
1360
  { id: 'get-pr-stats', type: 'action.run_command', label: 'PR Stats', config: { command: 'gh pr list --state all --json number,state --limit 50' }, position: { x: 400, y: 200 } },
1361
1361
  { id: 'get-agent-stats', type: 'action.run_command', label: 'Agent Stats', config: { command: 'bosun agent stats --json' }, position: { x: 650, y: 200 } },
1362
1362
  { id: 'generate-report', type: 'action.run_agent', label: 'Generate Report', config: { prompt: 'Generate daily activity summary' }, position: { x: 400, y: 380 } },
1363
- { id: 'send-report', type: 'notify.telegram', label: 'Send Report', config: { message: 'πŸ“Š Daily Bosun Report' }, position: { x: 400, y: 540 } },
1363
+ { id: 'send-report', type: 'notify.telegram', label: 'Send Report', config: { message: ':chart: Daily Bosun Report' }, position: { x: 400, y: 540 } },
1364
1364
  ],
1365
1365
  edges: [
1366
1366
  { id: 'e1', source: 'trigger', target: 'get-task-stats' }, { id: 'e2', source: 'trigger', target: 'get-pr-stats' },
@@ -2550,6 +2550,18 @@
2550
2550
  const [route, qs] = path.split('?');
2551
2551
  const params = new URLSearchParams(qs || '');
2552
2552
 
2553
+ // ── Auth / Fallback ──
2554
+ if (route === '/api/auth/fallback/status')
2555
+ return { ok: true, data: { enabled: false, hasSecret: false } };
2556
+ if (route === '/api/auth/fallback/login')
2557
+ return { ok: true, tokenIssued: true, auth: 'fallback' };
2558
+ if (route === '/api/auth/fallback/set')
2559
+ return { ok: true, data: { enabled: true, hasSecret: true } };
2560
+ if (route === '/api/auth/fallback/rotate')
2561
+ return { ok: true, data: { enabled: true, hasSecret: true } };
2562
+ if (route === '/api/auth/fallback/reset')
2563
+ return { ok: true, data: { enabled: false, hasSecret: false } };
2564
+
2553
2565
  // ── Status & Executor ──
2554
2566
  if (route === '/api/status')
2555
2567
  return { data: buildStatusSnapshot() };
@@ -3292,6 +3304,9 @@
3292
3304
  if (ses) ses.status = 'archived';
3293
3305
  return { ok: true, session: ses || null };
3294
3306
  }
3307
+ if (route.match(/^\/api\/sessions\/[^/]+\/missing_credentials$/)) {
3308
+ return { ok: false, changed: false, action: 'missing_credentials' };
3309
+ }
3295
3310
  if (route === '/api/sessions/create') {
3296
3311
  const id = 'ses-' + Math.random().toString(36).slice(2, 6);
3297
3312
  const s = {
@@ -3480,7 +3495,7 @@
3480
3495
  </head>
3481
3496
  <body>
3482
3497
  <!-- Demo mode banner -->
3483
- <div class="demo-banner">⚑ Bosun Demo Mode β€” Seed Data</div>
3498
+ <div class="demo-banner">:zap: Bosun Demo Mode β€” Seed Data</div>
3484
3499
 
3485
3500
  <!-- ═══ Telegram MiniApp (single view β€” loads immediately) ═══════════ -->
3486
3501
  <div class="demo-container">
@@ -3626,53 +3641,53 @@
3626
3641
  const SCREENS = {
3627
3642
  // ─── HOME ───
3628
3643
  home: {
3629
- title: 'πŸŽ›οΈ Bosun Control Center',
3644
+ title: ':sliders: Bosun Control Center',
3630
3645
  body: 'Choose a section to manage your automation fleet.',
3631
3646
  keyboard: [
3632
- [{ text: 'πŸ“Š Overview', go: 'overview' }, { text: '🧭 Tasks', go: 'tasks' }, { text: 'πŸ€– Agents', go: 'agents' }],
3633
- [{ text: 'βš™οΈ Executor', go: 'executor' }, { text: 'πŸ›° Routing', go: 'routing' }, { text: '🌳 Workspaces', go: 'workspaces' }],
3634
- [{ text: 'πŸ“ Logs & Git', go: 'logs_menu' }, { text: 'πŸ”Œ Integrations', go: 'integrations' }],
3635
- [{ text: 'πŸ”„ Refresh', cmd: 'status' }, { text: 'βœ– Close', action: 'close' }],
3647
+ [{ text: ':chart: Overview', go: 'overview' }, { text: ':compass: Tasks', go: 'tasks' }, { text: ':bot: Agents', go: 'agents' }],
3648
+ [{ text: ':settings: Executor', go: 'executor' }, { text: ':server: Routing', go: 'routing' }, { text: ':git: Workspaces', go: 'workspaces' }],
3649
+ [{ text: ':folder: Logs & Git', go: 'logs_menu' }, { text: ':plug: Integrations', go: 'integrations' }],
3650
+ [{ text: ':refresh: Refresh', cmd: 'status' }, { text: ':close: Close', action: 'close' }],
3636
3651
  ],
3637
3652
  },
3638
3653
 
3639
3654
  // ─── OVERVIEW (sub-menu) ───
3640
3655
  overview: {
3641
- title: 'πŸ“Š Overview',
3656
+ title: ':chart: Overview',
3642
3657
  parent: 'home',
3643
3658
  body: 'Live status, health, and presence dashboards.',
3644
3659
  keyboard: [
3645
- [{ text: 'πŸ“Š Status', cmd: 'status' }, { text: 'πŸ“‹ Tasks', cmd: 'tasks' }, { text: 'πŸ€– Agents', cmd: 'agents' }],
3646
- [{ text: 'πŸ₯ Health', cmd: 'health' }, { text: '⚠️ Anomalies', cmd: 'anomalies' }, { text: 'πŸ‘ Presence', cmd: 'presence' }],
3647
- [{ text: 'πŸ“ Logs', cmd: 'logs' }, { text: 'πŸ“ˆ Metrics', cmd: 'metrics' }],
3660
+ [{ text: ':chart: Status', cmd: 'status' }, { text: ':clipboard: Tasks', cmd: 'tasks' }, { text: ':bot: Agents', cmd: 'agents' }],
3661
+ [{ text: ':heart: Health', cmd: 'health' }, { text: ':alert: Anomalies', cmd: 'anomalies' }, { text: ':eye: Presence', cmd: 'presence' }],
3662
+ [{ text: ':edit: Logs', cmd: 'logs' }, { text: ':chart: Metrics', cmd: 'metrics' }],
3648
3663
  ],
3649
3664
  },
3650
3665
 
3651
3666
  // ─── TASKS (sub-menu with deeper levels) ───
3652
3667
  tasks: {
3653
- title: '🧭 Task Operations',
3668
+ title: ':compass: Task Operations',
3654
3669
  parent: 'home',
3655
3670
  body: 'Pause/resume, plan, retry, cleanup, and guided starts.',
3656
3671
  keyboard: [
3657
- [{ text: '⏸ Pause', cmd: 'pause' }, { text: '▢️ Resume', cmd: 'resume' }, { text: 'πŸ”„ Restart', cmd: 'restart' }],
3658
- [{ text: 'πŸ“‹ List Tasks', cmd: 'tasks' }, { text: '🧹 Cleanup', cmd: 'cleanup' }, { text: 'πŸ“Š Status', cmd: 'status' }],
3659
- [{ text: 'πŸ—ΊοΈ Planner', go: 'planner' }, { text: 'πŸ” Retry', go: 'retry' }, { text: 'βš™οΈ Executor', go: 'executor' }],
3660
- [{ text: 'πŸ—‚ Task Lists', go: 'task_lists' }, { text: '▢️ Start Task', cmd: 'starttask' }],
3672
+ [{ text: ':pause: Pause', cmd: 'pause' }, { text: ':play: Resume', cmd: 'resume' }, { text: ':refresh: Restart', cmd: 'restart' }],
3673
+ [{ text: ':clipboard: List Tasks', cmd: 'tasks' }, { text: ':trash: Cleanup', cmd: 'cleanup' }, { text: ':chart: Status', cmd: 'status' }],
3674
+ [{ text: ':grid: Planner', go: 'planner' }, { text: ':repeat: Retry', go: 'retry' }, { text: ':settings: Executor', go: 'executor' }],
3675
+ [{ text: ':folder: Task Lists', go: 'task_lists' }, { text: ':play: Start Task', cmd: 'starttask' }],
3661
3676
  ],
3662
3677
  },
3663
3678
  task_lists: {
3664
- title: 'πŸ—‚ Task Lists',
3679
+ title: ':folder: Task Lists',
3665
3680
  parent: 'tasks',
3666
3681
  body: 'Browse tasks by kanban status.',
3667
3682
  keyboard: [
3668
- [{ text: 'πŸ“₯ Backlog', cmd: 'tasks_backlog' }, { text: 'πŸ“ Draft', cmd: 'tasks_draft' }],
3669
- [{ text: 'βœ… Todo', cmd: 'tasks_todo' }, { text: '🚧 Active', cmd: 'tasks_active' }],
3670
- [{ text: 'πŸ” Review', cmd: 'tasks_review' }, { text: 'β›” Blocked', cmd: 'tasks_blocked' }],
3671
- [{ text: '🏁 Done', cmd: 'tasks_done' }],
3683
+ [{ text: ':download: Backlog', cmd: 'tasks_backlog' }, { text: ':edit: Draft', cmd: 'tasks_draft' }],
3684
+ [{ text: ':check: Todo', cmd: 'tasks_todo' }, { text: ':alert: Active', cmd: 'tasks_active' }],
3685
+ [{ text: ':search: Review', cmd: 'tasks_review' }, { text: ':ban: Blocked', cmd: 'tasks_blocked' }],
3686
+ [{ text: ':flag: Done', cmd: 'tasks_done' }],
3672
3687
  ],
3673
3688
  },
3674
3689
  planner: {
3675
- title: 'πŸ—ΊοΈ Task Planner',
3690
+ title: ':grid: Task Planner',
3676
3691
  parent: 'tasks',
3677
3692
  body: 'Trigger the planner to seed new tasks from the backlog.',
3678
3693
  keyboard: [
@@ -3680,7 +3695,7 @@
3680
3695
  ],
3681
3696
  },
3682
3697
  retry: {
3683
- title: 'πŸ” Fresh Retry',
3698
+ title: ':repeat: Fresh Retry',
3684
3699
  parent: 'tasks',
3685
3700
  body: 'Start a fresh session retry for the active task.',
3686
3701
  keyboard: [
@@ -3690,17 +3705,17 @@
3690
3705
 
3691
3706
  // ─── AGENTS (sub-menu with deeper levels) ───
3692
3707
  agents: {
3693
- title: 'πŸ€– Agents',
3708
+ title: ':bot: Agents',
3694
3709
  parent: 'home',
3695
3710
  body: 'Monitor and steer running agents.',
3696
3711
  keyboard: [
3697
- [{ text: 'πŸ€– Agents', cmd: 'agents' }, { text: 'πŸ“‹ Tasks', cmd: 'tasks' }, { text: 'πŸ“Š Status', cmd: 'status' }],
3698
- [{ text: 'πŸ“‚ Agent Logs', go: 'agent_logs' }, { text: '🧡 Threads', go: 'threads' }, { text: '🧠 History', cmd: 'history' }],
3699
- [{ text: '🧭 Steer', cmd: 'steer' }, { text: 'πŸ›‘ Stop', cmd: 'stop' }, { text: 'πŸ›° Background', go: 'background' }],
3712
+ [{ text: ':bot: Agents', cmd: 'agents' }, { text: ':clipboard: Tasks', cmd: 'tasks' }, { text: ':chart: Status', cmd: 'status' }],
3713
+ [{ text: ':folder: Agent Logs', go: 'agent_logs' }, { text: ':link: Threads', go: 'threads' }, { text: ':cpu: History', cmd: 'history' }],
3714
+ [{ text: ':compass: Steer', cmd: 'steer' }, { text: ':close: Stop', cmd: 'stop' }, { text: ':server: Background', go: 'background' }],
3700
3715
  ],
3701
3716
  },
3702
3717
  agent_logs: {
3703
- title: 'πŸ“‚ Agent Logs',
3718
+ title: ':folder: Agent Logs',
3704
3719
  parent: 'agents',
3705
3720
  body: 'View logs from specific worktree agent sessions.',
3706
3721
  keyboard: [
@@ -3709,7 +3724,7 @@
3709
3724
  ],
3710
3725
  },
3711
3726
  threads: {
3712
- title: '🧡 Threads',
3727
+ title: ':link: Threads',
3713
3728
  parent: 'agents',
3714
3729
  body: 'Manage persistent agent threads.',
3715
3730
  keyboard: [
@@ -3718,7 +3733,7 @@
3718
3733
  ],
3719
3734
  },
3720
3735
  threads_kill: {
3721
- title: 'πŸ—‘ Kill Thread',
3736
+ title: ':trash: Kill Thread',
3722
3737
  parent: 'threads',
3723
3738
  body: 'Select a thread to invalidate.',
3724
3739
  keyboard: [
@@ -3726,27 +3741,27 @@
3726
3741
  ],
3727
3742
  },
3728
3743
  background: {
3729
- title: 'πŸ›° Background Mode',
3744
+ title: ':server: Background Mode',
3730
3745
  parent: 'agents',
3731
3746
  body: 'Run tasks silently or background the active agent.',
3732
3747
  keyboard: [
3733
3748
  [{ text: 'Background Active', cmd: 'background_active' }, { text: 'New Background Task', cmd: 'background_new' }],
3734
- [{ text: '🧭 Steer', cmd: 'steer' }, { text: 'πŸ›‘ Stop', cmd: 'stop' }],
3749
+ [{ text: ':compass: Steer', cmd: 'steer' }, { text: ':close: Stop', cmd: 'stop' }],
3735
3750
  ],
3736
3751
  },
3737
3752
 
3738
3753
  // ─── EXECUTOR (sub-menu) ───
3739
3754
  executor: {
3740
- title: 'βš™οΈ Executor',
3755
+ title: ':settings: Executor',
3741
3756
  parent: 'home',
3742
3757
  body: 'Executor status, slots, and tuning.',
3743
3758
  keyboard: [
3744
3759
  [{ text: 'Status', cmd: 'executor' }, { text: 'Slots', cmd: 'executor_slots' }, { text: 'Mode', cmd: 'executor_mode' }],
3745
- [{ text: 'πŸ“Š Max Parallel', go: 'maxparallel' }, { text: '⏸ Pause', cmd: 'pause' }, { text: '▢️ Resume', cmd: 'resume' }],
3760
+ [{ text: ':chart: Max Parallel', go: 'maxparallel' }, { text: ':pause: Pause', cmd: 'pause' }, { text: ':play: Resume', cmd: 'resume' }],
3746
3761
  ],
3747
3762
  },
3748
3763
  maxparallel: {
3749
- title: 'πŸ“Š Max Parallel',
3764
+ title: ':chart: Max Parallel',
3750
3765
  parent: 'executor',
3751
3766
  body: 'Set the max concurrent task slots.',
3752
3767
  keyboard: [
@@ -3758,17 +3773,17 @@
3758
3773
 
3759
3774
  // ─── ROUTING (sub-menu with deeper levels) ───
3760
3775
  routing: {
3761
- title: 'πŸ›° Routing & Models',
3776
+ title: ':server: Routing & Models',
3762
3777
  parent: 'home',
3763
3778
  body: 'Control model routing, SDKs, and workspace routing.',
3764
3779
  keyboard: [
3765
- [{ text: 'πŸ€– Model', go: 'model' }, { text: 'πŸ“¦ SDK', go: 'sdk' }, { text: 'πŸ“‹ Kanban', cmd: 'kanban' }],
3766
- [{ text: '🌍 Region', go: 'region' }, { text: '♻️ Auto Backlog', cmd: 'autobacklog' }, { text: 'πŸ“ Requirements', cmd: 'requirements' }],
3767
- [{ text: '🎯 Route Task', cmd: 'route_task' }, { text: 'πŸ₯ Health', cmd: 'health' }],
3780
+ [{ text: ':bot: Model', go: 'model' }, { text: ':box: SDK', go: 'sdk' }, { text: ':clipboard: Kanban', cmd: 'kanban' }],
3781
+ [{ text: ':globe: Region', go: 'region' }, { text: ':repeat: Auto Backlog', cmd: 'autobacklog' }, { text: ':ruler: Requirements', cmd: 'requirements' }],
3782
+ [{ text: ':target: Route Task', cmd: 'route_task' }, { text: ':heart: Health', cmd: 'health' }],
3768
3783
  ],
3769
3784
  },
3770
3785
  model: {
3771
- title: 'πŸ€– Model Override',
3786
+ title: ':bot: Model Override',
3772
3787
  parent: 'routing',
3773
3788
  body: 'Override the executor model for the next run.',
3774
3789
  keyboard: [
@@ -3777,7 +3792,7 @@
3777
3792
  ],
3778
3793
  },
3779
3794
  sdk: {
3780
- title: 'πŸ“¦ SDK Selection',
3795
+ title: ':box: SDK Selection',
3781
3796
  parent: 'routing',
3782
3797
  body: 'Choose which SDK backend to use.',
3783
3798
  keyboard: [
@@ -3786,48 +3801,48 @@
3786
3801
  ],
3787
3802
  },
3788
3803
  region: {
3789
- title: '🌍 Region',
3804
+ title: ':globe: Region',
3790
3805
  parent: 'routing',
3791
3806
  body: 'Set the execution region for Codex.',
3792
3807
  keyboard: [
3793
3808
  [{ text: 'πŸ‡ΊπŸ‡Έ US East', cmd: 'region_us' }, { text: 'πŸ‡ΈπŸ‡ͺ EU North', cmd: 'region_eu' }],
3794
- [{ text: '🌐 Auto (default)', cmd: 'region_auto' }],
3809
+ [{ text: ':globe: Auto (default)', cmd: 'region_auto' }],
3795
3810
  ],
3796
3811
  },
3797
3812
 
3798
3813
  // ─── WORKSPACES ───
3799
3814
  workspaces: {
3800
- title: '🌳 Workspaces',
3815
+ title: ':git: Workspaces',
3801
3816
  parent: 'home',
3802
3817
  body: 'Manage git worktrees and workspaces.',
3803
3818
  keyboard: [
3804
- [{ text: '🌳 Worktrees', cmd: 'worktrees' }, { text: '🌿 Branches', cmd: 'branches' }],
3805
- [{ text: 'πŸ“ Git Diff', cmd: 'diff' }, { text: 'πŸ“– Git Log', cmd: 'git_log' }],
3806
- [{ text: '🧹 Cleanup', cmd: 'cleanup' }],
3819
+ [{ text: ':git: Worktrees', cmd: 'worktrees' }, { text: ':git: Branches', cmd: 'branches' }],
3820
+ [{ text: ':edit: Git Diff', cmd: 'diff' }, { text: ':file: Git Log', cmd: 'git_log' }],
3821
+ [{ text: ':trash: Cleanup', cmd: 'cleanup' }],
3807
3822
  ],
3808
3823
  },
3809
3824
 
3810
3825
  // ─── LOGS & GIT ───
3811
3826
  logs_menu: {
3812
- title: 'πŸ“ Logs & Git',
3827
+ title: ':folder: Logs & Git',
3813
3828
  parent: 'home',
3814
3829
  body: 'System logs, agent logs, and git operations.',
3815
3830
  keyboard: [
3816
- [{ text: 'πŸ“œ Recent Logs', cmd: 'logs' }, { text: 'πŸ“‚ Agent Logs', go: 'agent_logs' }],
3817
- [{ text: 'πŸ“ Git Diff', cmd: 'diff' }, { text: 'πŸ“– Git Log', cmd: 'git_log' }],
3818
- [{ text: 'πŸ’» Shell', cmd: 'shell' }],
3831
+ [{ text: ':file: Recent Logs', cmd: 'logs' }, { text: ':folder: Agent Logs', go: 'agent_logs' }],
3832
+ [{ text: ':edit: Git Diff', cmd: 'diff' }, { text: ':file: Git Log', cmd: 'git_log' }],
3833
+ [{ text: ':monitor: Shell', cmd: 'shell' }],
3819
3834
  ],
3820
3835
  },
3821
3836
 
3822
3837
  // ─── INTEGRATIONS ───
3823
3838
  integrations: {
3824
- title: 'πŸ”Œ Integrations',
3839
+ title: ':plug: Integrations',
3825
3840
  parent: 'home',
3826
3841
  body: 'Connected services and configuration.',
3827
3842
  keyboard: [
3828
- [{ text: 'πŸ™ GitHub', cmd: 'integration_github' }, { text: 'πŸ“± Telegram', cmd: 'integration_telegram' }],
3829
- [{ text: 'πŸ’» VS Code', cmd: 'integration_vscode' }, { text: 'πŸ“± WhatsApp', cmd: 'integration_whatsapp' }],
3830
- [{ text: 'πŸ“¦ Container', cmd: 'container' }, { text: 'πŸ”§ SDK Status', cmd: 'sdk_status' }],
3843
+ [{ text: ':git: GitHub', cmd: 'integration_github' }, { text: ':phone: Telegram', cmd: 'integration_telegram' }],
3844
+ [{ text: ':monitor: VS Code', cmd: 'integration_vscode' }, { text: ':phone: WhatsApp', cmd: 'integration_whatsapp' }],
3845
+ [{ text: ':box: Container', cmd: 'container' }, { text: ':settings: SDK Status', cmd: 'sdk_status' }],
3831
3846
  ],
3832
3847
  },
3833
3848
  };
@@ -3837,194 +3852,194 @@
3837
3852
  // ══════════════════════════════════════════════════════════════════
3838
3853
 
3839
3854
  const CMDS = {
3840
- status: () => `<strong>πŸ“Š Fleet Status</strong> β€” <code>● RUNNING</code><br/><br/>`+
3841
- `⏱ Uptime: <code>6d 14h 22m</code><br/>`+
3842
- `πŸ”§ Mode: <code>internal</code><br/>`+
3843
- `⚑ Max Parallel: <code>6</code><br/>`+
3844
- `πŸ“‚ Repos: <code>bosun</code> + <code>virtengine</code><br/><br/>`+
3855
+ status: () => `<strong>:chart: Fleet Status</strong> β€” <code>● RUNNING</code><br/><br/>`+
3856
+ `:clock: Uptime: <code>6d 14h 22m</code><br/>`+
3857
+ `:settings: Mode: <code>internal</code><br/>`+
3858
+ `:zap: Max Parallel: <code>6</code><br/>`+
3859
+ `:folder: Repos: <code>bosun</code> + <code>virtengine</code><br/><br/>`+
3845
3860
  `<strong>This Week</strong><br/>`+
3846
- `βœ… Completed: <strong>27</strong> Β· πŸ”€ PRs merged: <strong>22</strong> Β· ❌ Failures: <strong>2</strong> (auto-retried)<br/><br/>`+
3861
+ `:check: Completed: <strong>27</strong> Β· :git: PRs merged: <strong>22</strong> Β· :close: Failures: <strong>2</strong> (auto-retried)<br/><br/>`+
3847
3862
  `<strong>Executors</strong><br/>`+
3848
- `β”œβ”€ <code>copilot-claude</code> 🟒 load: 67% tasks: 16<br/>`+
3849
- `└─ <code>codex-default</code> 🟒 load: 42% tasks: 11`,
3863
+ `β”œβ”€ <code>copilot-claude</code> :dot: load: 67% tasks: 16<br/>`+
3864
+ `└─ <code>codex-default</code> :dot: load: 42% tasks: 11`,
3850
3865
 
3851
3866
  tasks: () => {
3852
- if (!TASKS.length) return '<strong>πŸ“‹ Tasks</strong><br/>No tasks found.';
3853
- const icons = { running: 'πŸ”΅', queued: '⏳', done: 'βœ…', failed: '❌', cancelled: 'βšͺ' };
3854
- return '<strong>πŸ“‹ Active Tasks</strong><br/><br/>' +
3867
+ if (!TASKS.length) return '<strong>:clipboard: Tasks</strong><br/>No tasks found.';
3868
+ const icons = { running: ':dot:', queued: ':clock:', done: ':check:', failed: ':close:', cancelled: ':dot:' };
3869
+ return '<strong>:clipboard: Active Tasks</strong><br/><br/>' +
3855
3870
  TASKS.map(t => `${icons[t.status]||'Β·'} <strong>${t.title}</strong> β€” <code>${t.status}</code>`).join('<br/>');
3856
3871
  },
3857
3872
 
3858
3873
  agents: () => {
3859
- if (!AGENTS.length) return '<strong>πŸ€– Agents</strong><br/>No agents configured.';
3860
- return '<strong>πŸ€– Agent Pool</strong><br/><br/>' +
3874
+ if (!AGENTS.length) return '<strong>:bot: Agents</strong><br/>No agents configured.';
3875
+ return '<strong>:bot: Agent Pool</strong><br/><br/>' +
3861
3876
  AGENTS.map(a => {
3862
- const s = a.healthy === false ? 'πŸ”΄' : '🟒';
3877
+ const s = a.healthy === false ? ':dot:' : ':dot:';
3863
3878
  return `${s} <strong>${a.name}</strong> β€” ${a.provider||'?'} Β· <code>${a.model||'?'}</code>`;
3864
3879
  }).join('<br/>');
3865
3880
  },
3866
3881
 
3867
- health: () => `<strong>πŸ’š System Health</strong> β€” All Clear<br/><br/>`+
3868
- `βœ… GitHub API <code>142ms</code><br/>`+
3869
- `βœ… Telegram Bot <code>89ms</code><br/>`+
3870
- `βœ… Codex SDK <code>234ms</code><br/>`+
3871
- `βœ… Copilot SDK <code>178ms</code><br/>`+
3872
- `βœ… Shared State <code>12ms</code><br/>`+
3873
- `βœ… Worktree Manager <code>45ms</code>`,
3882
+ health: () => `<strong>:heart: System Health</strong> β€” All Clear<br/><br/>`+
3883
+ `:check: GitHub API <code>142ms</code><br/>`+
3884
+ `:check: Telegram Bot <code>89ms</code><br/>`+
3885
+ `:check: Codex SDK <code>234ms</code><br/>`+
3886
+ `:check: Copilot SDK <code>178ms</code><br/>`+
3887
+ `:check: Shared State <code>12ms</code><br/>`+
3888
+ `:check: Worktree Manager <code>45ms</code>`,
3874
3889
 
3875
- anomalies: () => `<strong>πŸ” Anomaly Detector</strong> β€” <code>All Clear</code><br/><br/>`+
3876
- `βœ… No stuck agents<br/>βœ… No repeated lint failures<br/>βœ… No push loop detected<br/>βœ… No memory pressure<br/><br/>`+
3890
+ anomalies: () => `<strong>:search: Anomaly Detector</strong> β€” <code>All Clear</code><br/><br/>`+
3891
+ `:check: No stuck agents<br/>:check: No repeated lint failures<br/>:check: No push loop detected<br/>:check: No memory pressure<br/><br/>`+
3877
3892
  `Streak: <code>14</code> clean checks<br/>Last anomaly: <em>none today</em>`,
3878
3893
 
3879
- presence: () => `<strong>πŸ‘₯ Agent Presence</strong><br/><br/>`+
3880
- `🟒 <strong>workstation-1</strong> (this machine)<br/>`+
3894
+ presence: () => `<strong>:users: Agent Presence</strong><br/><br/>`+
3895
+ `:dot: <strong>workstation-1</strong> (this machine)<br/>`+
3881
3896
  ` β”œβ”€ copilot-claude: busy (#44)<br/>`+
3882
3897
  ` └─ codex-default: busy (#45)<br/><br/>`+
3883
3898
  `Fleet: 1 workstation, 2 agents online`,
3884
3899
 
3885
- metrics: () => `<strong>πŸ“ˆ Fleet Metrics</strong> (24h)<br/><br/>`+
3900
+ metrics: () => `<strong>:chart: Fleet Metrics</strong> (24h)<br/><br/>`+
3886
3901
  `<strong>Tasks</strong><br/>β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–‘ 89% β€” 47 completed<br/>β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 6% β€” 3 failed<br/><br/>`+
3887
3902
  `<strong>PRs</strong><br/>Created: 52 | Merged: 44 | CI failures: 8 (auto-fixed: 6)`,
3888
3903
 
3889
3904
  logs: () => {
3890
- if (!LOGS.length) return '<strong>πŸ“œ Logs</strong><br/>No recent logs.';
3891
- let html = '<strong>πŸ“œ Recent Logs</strong><br/><br/><pre>';
3905
+ if (!LOGS.length) return '<strong>:file: Logs</strong><br/>No recent logs.';
3906
+ let html = '<strong>:file: Recent Logs</strong><br/><br/><pre>';
3892
3907
  LOGS.slice(-8).forEach(l => {
3893
3908
  html += `[${(l.level||'info').toUpperCase().padEnd(5)}] ${(l.source||'?').padEnd(10)} ${l.msg}\n`;
3894
3909
  });
3895
3910
  return html + '</pre>';
3896
3911
  },
3897
3912
 
3898
- executor: () => `<strong>βš™οΈ Executor Pool</strong><br/><br/>`+
3913
+ executor: () => `<strong>:settings: Executor Pool</strong><br/><br/>`+
3899
3914
  `Mode: <code>internal</code><br/>Max parallel: <code>6</code><br/><br/>`+
3900
3915
  `1. copilot-claude (50%) β€” COPILOT:CLAUDE_OPUS_4_6<br/>`+
3901
3916
  `2. codex-default (50%) β€” CODEX:DEFAULT`,
3902
3917
 
3903
- executor_slots: () => `<strong>βš™οΈ Executor Slots</strong><br/><br/>Active: <strong>2</strong> / 6<br/>Queued: <strong>3</strong><br/>Free: <strong>4</strong>`,
3904
- executor_mode: () => `<strong>βš™οΈ Mode:</strong> <code>internal</code><br/><br/>Available: internal, container, remote`,
3918
+ executor_slots: () => `<strong>:settings: Executor Slots</strong><br/><br/>Active: <strong>2</strong> / 6<br/>Queued: <strong>3</strong><br/>Free: <strong>4</strong>`,
3919
+ executor_mode: () => `<strong>:settings: Mode:</strong> <code>internal</code><br/><br/>Available: internal, container, remote`,
3905
3920
 
3906
3921
  worktrees: () => {
3907
3922
  const wt = INFRA.worktrees || [];
3908
- if (!wt.length) return '<strong>🌳 Worktrees</strong><br/>No worktrees found.';
3909
- return '<strong>🌳 Git Worktrees</strong><br/><br/>' +
3910
- wt.map(w => `πŸ“ <strong>${w.branch||w.path}</strong> β€” ${w.path}`).join('<br/>') +
3923
+ if (!wt.length) return '<strong>:git: Worktrees</strong><br/>No worktrees found.';
3924
+ return '<strong>:git: Git Worktrees</strong><br/><br/>' +
3925
+ wt.map(w => `:folder: <strong>${w.branch||w.path}</strong> β€” ${w.path}`).join('<br/>') +
3911
3926
  `<br/><br/>Total: ${wt.length} worktrees`;
3912
3927
  },
3913
3928
 
3914
- branches: () => `<strong>🌿 Active Branches</strong><br/><br/>`+
3929
+ branches: () => `<strong>:git: Active Branches</strong><br/><br/>`+
3915
3930
  `<strong>virtengine/bosun</strong><br/>`+
3916
- `β”œβ”€ <code>docs/bosun-improvement-plan</code> πŸ”΅ active (PR #17)<br/>`+
3917
- `β”œβ”€ <code>bs/12-error-correlation</code> πŸ”΅ active<br/>`+
3918
- `β”œβ”€ <code>bs/11-agent-logging-enrich</code> πŸ”΅ active<br/>`+
3919
- `β”œβ”€ <code>fix/kanban-repo-filters</code> πŸ”΅ active<br/>`+
3920
- `└─ <code>bs/13-weekly-report</code> ⏳ draft<br/><br/>`+
3931
+ `β”œβ”€ <code>docs/bosun-improvement-plan</code> :dot: active (PR #17)<br/>`+
3932
+ `β”œβ”€ <code>bs/12-error-correlation</code> :dot: active<br/>`+
3933
+ `β”œβ”€ <code>bs/11-agent-logging-enrich</code> :dot: active<br/>`+
3934
+ `β”œβ”€ <code>fix/kanban-repo-filters</code> :dot: active<br/>`+
3935
+ `└─ <code>bs/13-weekly-report</code> :clock: draft<br/><br/>`+
3921
3936
  `<strong>virtengine/virtengine</strong><br/>`+
3922
- `β”œβ”€ <code>ve/760-market-order-expiry</code> πŸ”΅ active<br/>`+
3923
- `β”œβ”€ <code>ve/761-escrow-batch</code> πŸ”΅ active<br/>`+
3924
- `β”œβ”€ <code>ve/762-veid-token-fix</code> πŸ“ in review<br/>`+
3937
+ `β”œβ”€ <code>ve/760-market-order-expiry</code> :dot: active<br/>`+
3938
+ `β”œβ”€ <code>ve/761-escrow-batch</code> :dot: active<br/>`+
3939
+ `β”œβ”€ <code>ve/762-veid-token-fix</code> :edit: in review<br/>`+
3925
3940
  `└─ <code>ve/751-security-bulk-remediation</code> (merged, PR #751)`,
3926
3941
 
3927
- diff: () => `<strong>πŸ“ Git Diff Summary</strong> (staged)<br/><br/>`+
3942
+ diff: () => `<strong>:edit: Git Diff Summary</strong> (staged)<br/><br/>`+
3928
3943
  `<code>bosun/ui/components/kanban-board.js</code> +124 -18<br/>`+
3929
3944
  `<code>bosun/ui/styles/kanban.css</code> +156 -3<br/>`+
3930
3945
  `<code>bosun/ui/demo.html</code> +312 -89<br/><br/>`+
3931
3946
  `3 files changed, 592 insertions(+), 110 deletions(-)`,
3932
3947
 
3933
- git_log: () => `<strong>πŸ“– Git Log</strong><br/><br/>`+
3948
+ git_log: () => `<strong>:file: Git Log</strong><br/><br/>`+
3934
3949
  `<code>a1b2c3d</code> feat(kanban): add repo badge and filter bar (PR #17)<br/>`+
3935
3950
  `<code>4e5f6a7</code> fix(ui): health card text contrast<br/>`+
3936
3951
  `<code>8b9c0d1</code> feat(site): landing page redesign<br/>`+
3937
3952
  `<code>e2f3a4b</code> fix(security): bulk remediation 287/300 alerts (PR #751)<br/>`+
3938
3953
  `<code>f5a6b7c</code> feat(oauth): GitHub App auth setup (PR #10)`,
3939
3954
 
3940
- shell: () => `<strong>πŸ’» Shell</strong> β€” <code>ls logs/</code><br/><br/>`+
3955
+ shell: () => `<strong>:monitor: Shell</strong> β€” <code>ls logs/</code><br/><br/>`+
3941
3956
  `<code>daemon.log monitor.log agent-42.log agent-43.log agent-44.log telegram.log</code>`,
3942
3957
 
3943
- history: () => `<strong>🧠 Session History</strong><br/><br/>`+
3958
+ history: () => `<strong>:cpu: Session History</strong><br/><br/>`+
3944
3959
  `β”œβ”€ Session #1: Landing page redesign (bosun PR #17) β€” <em>in progress</em><br/>`+
3945
3960
  `β”œβ”€ Session #2: Market order expiry (virtengine) β€” <em>in progress</em><br/>`+
3946
- `β”œβ”€ Session #6: Security bulk remediation (PR #751) β€” βœ… done<br/>`+
3961
+ `β”œβ”€ Session #6: Security bulk remediation (PR #751) β€” :check: done<br/>`+
3947
3962
  `β”œβ”€ Session #7: Kanban filters + repo badge β€” <em>in progress</em><br/>`+
3948
- `└─ Session #8: MiniApp redesign (PR #754) β€” βœ… done<br/><br/>`+
3963
+ `└─ Session #8: MiniApp redesign (PR #754) β€” :check: done<br/><br/>`+
3949
3964
  `Total: 8 sessions this week, 142 turns`,
3950
3965
 
3951
- threads: () => `<strong>🧡 Active Threads</strong><br/><br/>`+
3952
- `β”œβ”€ Thread #1: <code>docs/bosun-improvement-plan</code> 🟒 (18 turns)<br/>`+
3953
- `β”œβ”€ Thread #2: <code>ve/760-market-order-expiry</code> 🟒 (12 turns)<br/>`+
3954
- `β”œβ”€ Thread #3: <code>bs/12-error-correlation</code> 🟒 (6 turns)<br/>`+
3955
- `└─ Thread #4: <code>primary-agent</code> 🟑 idle<br/><br/>`+
3966
+ threads: () => `<strong>:link: Active Threads</strong><br/><br/>`+
3967
+ `β”œβ”€ Thread #1: <code>docs/bosun-improvement-plan</code> :dot: (18 turns)<br/>`+
3968
+ `β”œβ”€ Thread #2: <code>ve/760-market-order-expiry</code> :dot: (12 turns)<br/>`+
3969
+ `β”œβ”€ Thread #3: <code>bs/12-error-correlation</code> :dot: (6 turns)<br/>`+
3970
+ `└─ Thread #4: <code>primary-agent</code> :dot: idle<br/><br/>`+
3956
3971
  `Total: 4 threads (3 active)`,
3957
- threads_clear: () => `<strong>🧡 Registry cleared.</strong> All idle threads removed.`,
3972
+ threads_clear: () => `<strong>:link: Registry cleared.</strong> All idle threads removed.`,
3958
3973
 
3959
- steer: () => `<strong>🧭 Steer Agent</strong><br/><br/>Use: <code>/steer &lt;directive&gt;</code><br/><br/>Example:<br/><code>/steer focus on adding tests first</code><br/><code>/steer skip the CLI and focus on keeper only</code>`,
3960
- stop: () => `<strong>πŸ›‘ Stop Agent</strong><br/><br/>No active agents running right now.`,
3974
+ steer: () => `<strong>:compass: Steer Agent</strong><br/><br/>Use: <code>/steer &lt;directive&gt;</code><br/><br/>Example:<br/><code>/steer focus on adding tests first</code><br/><code>/steer skip the CLI and focus on keeper only</code>`,
3975
+ stop: () => `<strong>:close: Stop Agent</strong><br/><br/>No active agents running right now.`,
3961
3976
 
3962
- kanban: () => `<strong>πŸ“Š Kanban Board</strong><br/><br/>πŸ“ Draft: 2<br/>πŸ“‹ Todo: 3<br/>πŸ”΅ In Progress: 4<br/>πŸ“ In Review: 2<br/>βœ… Done: 8<br/>❌ Error: 1<br/><br/>Total: 20 tasks across 2 repos`,
3977
+ kanban: () => `<strong>:chart: Kanban Board</strong><br/><br/>:edit: Draft: 2<br/>:clipboard: Todo: 3<br/>:dot: In Progress: 4<br/>:edit: In Review: 2<br/>:check: Done: 8<br/>:close: Error: 1<br/><br/>Total: 20 tasks across 2 repos`,
3963
3978
 
3964
- pause: () => `<strong>⏸ Task processing paused.</strong><br/><br/>No new tasks will be dispatched. Active tasks continue.<br/>Use Resume to restart.`,
3965
- resume: () => `<strong>▢️ Task processing resumed.</strong><br/><br/>Fleet is picking up tasks from the backlog.`,
3966
- restart: () => `<strong>πŸ”„ Restarting...</strong><br/><br/>No failed tasks to restart. All tasks healthy.`,
3967
- cleanup: () => `<strong>🧹 Cleanup Results</strong><br/><br/>βœ… 0 stale orchestrators removed<br/>βœ… 0 stuck pushes cleared<br/>βœ… 1 worktree pruned<br/>βœ… 0 orphaned branches cleaned`,
3968
- starttask: () => `<strong>▢️ Starting task #46</strong>...<br/><br/>πŸ“‹ docs: update provider guide<br/>πŸ€– Assigned to: <code>codex-default</code><br/>🌿 Branch: <code>ve/46-docs-provider-guide</code><br/><br/>βœ… Task dispatched.`,
3979
+ pause: () => `<strong>:pause: Task processing paused.</strong><br/><br/>No new tasks will be dispatched. Active tasks continue.<br/>Use Resume to restart.`,
3980
+ resume: () => `<strong>:play: Task processing resumed.</strong><br/><br/>Fleet is picking up tasks from the backlog.`,
3981
+ restart: () => `<strong>:refresh: Restarting...</strong><br/><br/>No failed tasks to restart. All tasks healthy.`,
3982
+ cleanup: () => `<strong>:trash: Cleanup Results</strong><br/><br/>:check: 0 stale orchestrators removed<br/>:check: 0 stuck pushes cleared<br/>:check: 1 worktree pruned<br/>:check: 0 orphaned branches cleaned`,
3983
+ starttask: () => `<strong>:play: Starting task #46</strong>...<br/><br/>:clipboard: docs: update provider guide<br/>:bot: Assigned to: <code>codex-default</code><br/>:git: Branch: <code>ve/46-docs-provider-guide</code><br/><br/>:check: Task dispatched.`,
3969
3984
 
3970
- plan3: () => `<strong>πŸ—ΊοΈ Planning 3 tasks...</strong><br/><br/>1. feat(hpc): GPU resource metering<br/>2. fix(provider): health check endpoint<br/>3. docs: update provider guide<br/><br/>βœ… 3 tasks queued.`,
3971
- plan5: () => `<strong>πŸ—ΊοΈ Planning 5 tasks...</strong><br/><br/>1. feat(hpc): GPU resource metering<br/>2. fix(provider): health check<br/>3. docs: update provider guide<br/>4. refactor(roles): simplify permissions<br/>5. test(escrow): settlement edge cases<br/><br/>βœ… 5 tasks queued.`,
3972
- plan10: () => `<strong>πŸ—ΊοΈ Planning 10 tasks...</strong><br/><br/>βœ… 10 tasks seeded from backlog and queued.`,
3985
+ plan3: () => `<strong>:grid: Planning 3 tasks...</strong><br/><br/>1. feat(hpc): GPU resource metering<br/>2. fix(provider): health check endpoint<br/>3. docs: update provider guide<br/><br/>:check: 3 tasks queued.`,
3986
+ plan5: () => `<strong>:grid: Planning 5 tasks...</strong><br/><br/>1. feat(hpc): GPU resource metering<br/>2. fix(provider): health check<br/>3. docs: update provider guide<br/>4. refactor(roles): simplify permissions<br/>5. test(escrow): settlement edge cases<br/><br/>:check: 5 tasks queued.`,
3987
+ plan10: () => `<strong>:grid: Planning 10 tasks...</strong><br/><br/>:check: 10 tasks seeded from backlog and queued.`,
3973
3988
 
3974
- retry_manual: () => `<strong>πŸ” Manual Retry</strong><br/><br/>Retrying active task with clean context... βœ… retry dispatched.`,
3975
- retry_stuck: () => `<strong>πŸ” Stuck Retry</strong><br/><br/>Detecting stuck state... No stuck agents found.`,
3976
- retry_ratelimit: () => `<strong>πŸ” Rate Limit Retry</strong><br/><br/>Waiting for rate limit cooldown... βœ… Retrying in 30s.`,
3989
+ retry_manual: () => `<strong>:repeat: Manual Retry</strong><br/><br/>Retrying active task with clean context... :check: retry dispatched.`,
3990
+ retry_stuck: () => `<strong>:repeat: Stuck Retry</strong><br/><br/>Detecting stuck state... No stuck agents found.`,
3991
+ retry_ratelimit: () => `<strong>:repeat: Rate Limit Retry</strong><br/><br/>Waiting for rate limit cooldown... :check: Retrying in 30s.`,
3977
3992
 
3978
3993
  // Task list statuses
3979
3994
  tasks_backlog: () => tasksByStatus('Backlog', ['queued']),
3980
- tasks_draft: () => '<strong>πŸ“ Draft Tasks</strong><br/><br/>No draft tasks.',
3995
+ tasks_draft: () => '<strong>:edit: Draft Tasks</strong><br/><br/>No draft tasks.',
3981
3996
  tasks_todo: () => tasksByStatus('Todo', ['queued']),
3982
3997
  tasks_active: () => tasksByStatus('Active', ['running']),
3983
- tasks_review: () => '<strong>πŸ” Review</strong><br/><br/>No tasks in review.',
3984
- tasks_blocked: () => '<strong>β›” Blocked</strong><br/><br/>No blocked tasks.',
3998
+ tasks_review: () => '<strong>:search: Review</strong><br/><br/>No tasks in review.',
3999
+ tasks_blocked: () => '<strong>:ban: Blocked</strong><br/><br/>No blocked tasks.',
3985
4000
  tasks_done: () => tasksByStatus('Done', ['done']),
3986
4001
 
3987
- agentlogs_escrow: () => `<strong>πŸ“‚ Agent Logs β€” docs/bosun-improvement-plan</strong><br/><br/><pre>[09:12] Starting session for bosun PR #17\n[09:14] Reading site/indexv2.html\n[09:18] Refactoring demo tabs for mobile\n[09:24] Added helm SVG hero\n[09:30] CI queued on PR #17</pre>`,
3988
- agentlogs_hpc: () => `<strong>πŸ“‚ Agent Logs β€” ve/760-market-order-expiry</strong><br/><br/><pre>[10:05] Starting session...\n[10:06] Reading x/market/keeper/order.go\n[10:10] Implementing auto-expiry TTL\n[10:15] Writing order_test.go...</pre>`,
3989
- agentlogs_docs: () => `<strong>πŸ“‚ Agent Logs β€” bs/12-error-correlation</strong><br/><br/><pre>[Queued β€” not yet started]</pre>`,
4002
+ agentlogs_escrow: () => `<strong>:folder: Agent Logs β€” docs/bosun-improvement-plan</strong><br/><br/><pre>[09:12] Starting session for bosun PR #17\n[09:14] Reading site/indexv2.html\n[09:18] Refactoring demo tabs for mobile\n[09:24] Added helm SVG hero\n[09:30] CI queued on PR #17</pre>`,
4003
+ agentlogs_hpc: () => `<strong>:folder: Agent Logs β€” ve/760-market-order-expiry</strong><br/><br/><pre>[10:05] Starting session...\n[10:06] Reading x/market/keeper/order.go\n[10:10] Implementing auto-expiry TTL\n[10:15] Writing order_test.go...</pre>`,
4004
+ agentlogs_docs: () => `<strong>:folder: Agent Logs β€” bs/12-error-correlation</strong><br/><br/><pre>[Queued β€” not yet started]</pre>`,
3990
4005
 
3991
- kill_thread_44: () => `<strong>πŸ—‘ Thread Invalidated</strong><br/><br/>Thread <code>ve/44-escrow-batch</code> has been invalidated. Session will restart on next dispatch.`,
3992
- kill_thread_45: () => `<strong>πŸ—‘ Thread Invalidated</strong><br/><br/>Thread <code>ve/45-hpc-gpu</code> has been invalidated.`,
4006
+ kill_thread_44: () => `<strong>:trash: Thread Invalidated</strong><br/><br/>Thread <code>ve/44-escrow-batch</code> has been invalidated. Session will restart on next dispatch.`,
4007
+ kill_thread_45: () => `<strong>:trash: Thread Invalidated</strong><br/><br/>Thread <code>ve/45-hpc-gpu</code> has been invalidated.`,
3993
4008
 
3994
- background_active: () => `<strong>πŸ›° Backgrounded</strong><br/><br/>Active agent moved to background mode. It will continue working silently.`,
3995
- background_new: () => `<strong>πŸ›° New Background Task</strong><br/><br/>Describe the task to run in background mode.`,
4009
+ background_active: () => `<strong>:server: Backgrounded</strong><br/><br/>Active agent moved to background mode. It will continue working silently.`,
4010
+ background_new: () => `<strong>:server: New Background Task</strong><br/><br/>Describe the task to run in background mode.`,
3996
4011
 
3997
4012
  mp_0: () => mpSet(0), mp_1: () => mpSet(1), mp_2: () => mpSet(2),
3998
4013
  mp_3: () => mpSet(3), mp_4: () => mpSet(4), mp_6: () => mpSet(6),
3999
4014
  mp_8: () => mpSet(8), mp_12: () => mpSet(12), mp_16: () => mpSet(16),
4000
4015
 
4001
- model_opus: () => `<strong>πŸ€– Model set:</strong> <code>claude-opus-4-6</code><br/>Next tasks will use this model.`,
4002
- model_o4: () => `<strong>πŸ€– Model set:</strong> <code>o4-mini</code>`,
4003
- model_gpt5: () => `<strong>πŸ€– Model set:</strong> <code>gpt-5.2-codex</code>`,
4004
- model_default: () => `<strong>πŸ€– Model reset to default</strong> routing policy.`,
4005
-
4006
- sdk_copilot: () => `<strong>πŸ“¦ SDK:</strong> <code>COPILOT</code> selected.`,
4007
- sdk_codex: () => `<strong>πŸ“¦ SDK:</strong> <code>CODEX</code> selected.`,
4008
- sdk_auto: () => `<strong>πŸ“¦ SDK:</strong> <code>AUTO</code> β€” round-robin.`,
4009
- sdk_status: () => `<strong>πŸ”§ SDK Status</strong><br/><br/>βœ… Copilot SDK: loaded<br/>βœ… Codex SDK: loaded<br/>βœ… GitHub CLI: authenticated<br/>βœ… Telegram API: connected`,
4010
-
4011
- region_us: () => `<strong>🌍 Region:</strong> <code>US East</code> selected.`,
4012
- region_eu: () => `<strong>🌍 Region:</strong> <code>EU North (Sweden)</code> selected.`,
4013
- region_auto: () => `<strong>🌍 Region:</strong> <code>Auto</code> β€” lowest latency.`,
4014
-
4015
- autobacklog: () => `<strong>♻️ Auto Backlog:</strong> Enabled<br/><br/>New GitHub issues matching label filters will be auto-added to the kanban backlog.`,
4016
- requirements: () => `<strong>πŸ“ Requirements:</strong><br/><br/>Task requirements are parsed from issue descriptions. Acceptance criteria extracted via LLM.`,
4017
- route_task: () => `<strong>🎯 Route Task</strong><br/><br/>Next queued task will be routed using weighted random policy across online executors.`,
4018
- container: () => `<strong>πŸ“¦ Container Status</strong><br/><br/>Container Mode: <code>disabled</code><br/>Available: Docker, Podman<br/><br/>Enable with <code>CONTAINER_MODE=docker</code>`,
4019
-
4020
- integration_github: () => `<strong>πŸ™ GitHub</strong> β€” βœ… Connected<br/><br/>Org: <code>virtengine</code><br/>Repos: <code>bosun</code>, <code>virtengine</code><br/>Kanban: bidirectional sync`,
4021
- integration_telegram: () => `<strong>πŸ“± Telegram</strong> β€” βœ… Bot active<br/><br/>Bot: <code>@bosun_bot</code><br/>MiniApp: enabled<br/>Sticky menu: active`,
4022
- integration_vscode: () => `<strong>πŸ’» VS Code</strong> β€” βœ… Copilot extension linked<br/><br/>Agent: copilot-claude<br/>MCP servers: connected`,
4023
- integration_whatsapp: () => `<strong>πŸ“± WhatsApp</strong> β€” Not configured<br/><br/>Run <code>bosun --whatsapp-auth</code> to set up.`,
4016
+ model_opus: () => `<strong>:bot: Model set:</strong> <code>claude-opus-4-6</code><br/>Next tasks will use this model.`,
4017
+ model_o4: () => `<strong>:bot: Model set:</strong> <code>o4-mini</code>`,
4018
+ model_gpt5: () => `<strong>:bot: Model set:</strong> <code>gpt-5.2-codex</code>`,
4019
+ model_default: () => `<strong>:bot: Model reset to default</strong> routing policy.`,
4020
+
4021
+ sdk_copilot: () => `<strong>:box: SDK:</strong> <code>COPILOT</code> selected.`,
4022
+ sdk_codex: () => `<strong>:box: SDK:</strong> <code>CODEX</code> selected.`,
4023
+ sdk_auto: () => `<strong>:box: SDK:</strong> <code>AUTO</code> β€” round-robin.`,
4024
+ sdk_status: () => `<strong>:settings: SDK Status</strong><br/><br/>:check: Copilot SDK: loaded<br/>:check: Codex SDK: loaded<br/>:check: GitHub CLI: authenticated<br/>:check: Telegram API: connected`,
4025
+
4026
+ region_us: () => `<strong>:globe: Region:</strong> <code>US East</code> selected.`,
4027
+ region_eu: () => `<strong>:globe: Region:</strong> <code>EU North (Sweden)</code> selected.`,
4028
+ region_auto: () => `<strong>:globe: Region:</strong> <code>Auto</code> β€” lowest latency.`,
4029
+
4030
+ autobacklog: () => `<strong>:repeat: Auto Backlog:</strong> Enabled<br/><br/>New GitHub issues matching label filters will be auto-added to the kanban backlog.`,
4031
+ requirements: () => `<strong>:ruler: Requirements:</strong><br/><br/>Task requirements are parsed from issue descriptions. Acceptance criteria extracted via LLM.`,
4032
+ route_task: () => `<strong>:target: Route Task</strong><br/><br/>Next queued task will be routed using weighted random policy across online executors.`,
4033
+ container: () => `<strong>:box: Container Status</strong><br/><br/>Container Mode: <code>disabled</code><br/>Available: Docker, Podman<br/><br/>Enable with <code>CONTAINER_MODE=docker</code>`,
4034
+
4035
+ integration_github: () => `<strong>:git: GitHub</strong> β€” :check: Connected<br/><br/>Org: <code>virtengine</code><br/>Repos: <code>bosun</code>, <code>virtengine</code><br/>Kanban: bidirectional sync`,
4036
+ integration_telegram: () => `<strong>:phone: Telegram</strong> β€” :check: Bot active<br/><br/>Bot: <code>@bosun_bot</code><br/>MiniApp: enabled<br/>Sticky menu: active`,
4037
+ integration_vscode: () => `<strong>:monitor: VS Code</strong> β€” :check: Copilot extension linked<br/><br/>Agent: copilot-claude<br/>MCP servers: connected`,
4038
+ integration_whatsapp: () => `<strong>:phone: WhatsApp</strong> β€” Not configured<br/><br/>Run <code>bosun --whatsapp-auth</code> to set up.`,
4024
4039
  };
4025
4040
 
4026
4041
  function tasksByStatus(label, statuses) {
4027
- const icons = { running: 'πŸ”΅', queued: '⏳', done: 'βœ…', failed: '❌', cancelled: 'βšͺ' };
4042
+ const icons = { running: ':dot:', queued: ':clock:', done: ':check:', failed: ':close:', cancelled: ':dot:' };
4028
4043
  const filtered = TASKS.filter(t => statuses.includes(t.status));
4029
4044
  if (!filtered.length) return `<strong>${label}</strong><br/><br/>No tasks with this status.`;
4030
4045
  return `<strong>${label} Tasks</strong> (${filtered.length})<br/><br/>` +
@@ -4032,7 +4047,7 @@
4032
4047
  }
4033
4048
 
4034
4049
  function mpSet(n) {
4035
- return `<strong>⚑ Max Parallel set to <code>${n}</code></strong><br/><br/>${n === 0 ? 'Task processing effectively paused.' : `Up to ${n} tasks will run concurrently.`}`;
4050
+ return `<strong>:zap: Max Parallel set to <code>${n}</code></strong><br/><br/>${n === 0 ? 'Task processing effectively paused.' : `Up to ${n} tasks will run concurrently.`}`;
4036
4051
  }
4037
4052
 
4038
4053
  // ══════════════════════════════════════════════════════════════════
@@ -4171,7 +4186,7 @@
4171
4186
  // Fallback: if ES modules fail to load within 12s, show an error
4172
4187
  setTimeout(function() {
4173
4188
  var el = document.getElementById('boot-loader');
4174
- if (el) el.innerHTML = '<div style="text-align:center;padding:40px;font-family:Inter,sans-serif;color:#94a3b8"><div style="font-size:18px;margin-bottom:8px">⚠️ Failed to load</div><div style="font-size:12px;color:#64748b">Try refreshing. If this persists, run npm install in the bosun directory.</div></div>';
4189
+ if (el) el.innerHTML = '<div style="text-align:center;padding:40px;font-family:Inter,sans-serif;color:#94a3b8"><div style="font-size:18px;margin-bottom:8px">:alert: Failed to load</div><div style="font-size:12px;color:#64748b">Try refreshing. If this persists, run npm install in the bosun directory.</div></div>';
4175
4190
  }, 12000);
4176
4191
 
4177
4192
  })();