bosun 0.36.2 → 0.36.4

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 (57) hide show
  1. package/agent-prompts.mjs +95 -0
  2. package/analyze-agent-work-helpers.mjs +308 -0
  3. package/analyze-agent-work.mjs +926 -0
  4. package/autofix.mjs +2 -0
  5. package/bosun.schema.json +101 -3
  6. package/codex-shell.mjs +85 -10
  7. package/desktop/main.mjs +871 -48
  8. package/desktop/preload.mjs +54 -1
  9. package/desktop-shortcut.mjs +90 -11
  10. package/git-editor-fix.mjs +273 -0
  11. package/mcp-registry.mjs +579 -0
  12. package/meeting-workflow-service.mjs +631 -0
  13. package/monitor.mjs +18 -103
  14. package/package.json +21 -2
  15. package/primary-agent.mjs +32 -12
  16. package/session-tracker.mjs +68 -0
  17. package/setup-web-server.mjs +20 -10
  18. package/setup.mjs +376 -83
  19. package/startup-service.mjs +51 -6
  20. package/stream-resilience.mjs +17 -7
  21. package/ui/app.js +164 -4
  22. package/ui/components/agent-selector.js +145 -1
  23. package/ui/components/chat-view.js +161 -15
  24. package/ui/components/session-list.js +2 -2
  25. package/ui/components/shared.js +188 -15
  26. package/ui/modules/icons.js +13 -0
  27. package/ui/modules/utils.js +44 -0
  28. package/ui/modules/voice-client-sdk.js +733 -0
  29. package/ui/modules/voice-overlay.js +128 -15
  30. package/ui/modules/voice.js +15 -6
  31. package/ui/setup.html +281 -81
  32. package/ui/styles/components.css +99 -3
  33. package/ui/styles/sessions.css +122 -14
  34. package/ui/styles.css +14 -0
  35. package/ui/tabs/agents.js +1 -1
  36. package/ui/tabs/chat.js +123 -14
  37. package/ui/tabs/control.js +16 -22
  38. package/ui/tabs/dashboard.js +85 -8
  39. package/ui/tabs/library.js +113 -17
  40. package/ui/tabs/settings.js +116 -2
  41. package/ui/tabs/tasks.js +388 -39
  42. package/ui/tabs/telemetry.js +0 -1
  43. package/ui/tabs/workflows.js +4 -0
  44. package/ui-server.mjs +400 -22
  45. package/update-check.mjs +41 -13
  46. package/voice-action-dispatcher.mjs +844 -0
  47. package/voice-agents-sdk.mjs +664 -0
  48. package/voice-auth-manager.mjs +164 -0
  49. package/voice-relay.mjs +1194 -0
  50. package/voice-tools.mjs +914 -0
  51. package/workflow-templates/agents.mjs +6 -2
  52. package/workflow-templates/github.mjs +154 -12
  53. package/workflow-templates.mjs +3 -0
  54. package/github-reconciler.mjs +0 -506
  55. package/merge-strategy.mjs +0 -1210
  56. package/pr-cleanup-daemon.mjs +0 -992
  57. package/workspace-reaper.mjs +0 -405
@@ -18,6 +18,12 @@ import {
18
18
  voiceToolCalls, voiceDuration,
19
19
  startVoiceSession, stopVoiceSession, interruptResponse,
20
20
  } from "./voice-client.js";
21
+ import {
22
+ sdkVoiceState, sdkVoiceTranscript, sdkVoiceResponse, sdkVoiceError,
23
+ sdkVoiceToolCalls, sdkVoiceDuration, sdkVoiceSdkActive,
24
+ startSdkVoiceSession, stopSdkVoiceSession, interruptSdkResponse,
25
+ sendSdkTextMessage, onSdkVoiceEvent,
26
+ } from "./voice-client-sdk.js";
21
27
  import {
22
28
  fallbackState, fallbackTranscript, fallbackResponse,
23
29
  fallbackError,
@@ -422,6 +428,53 @@ function injectOverlayStyles() {
422
428
  width: 162px;
423
429
  height: 162px;
424
430
  }
431
+ }
432
+ .voice-overlay.compact {
433
+ background: rgba(0, 0, 0, 0.93);
434
+ }
435
+ .voice-overlay.compact .voice-overlay-header {
436
+ padding: 12px 14px;
437
+ }
438
+ .voice-overlay.compact .voice-overlay-main {
439
+ padding: 62px 10px 10px;
440
+ flex-direction: column;
441
+ gap: 10px;
442
+ }
443
+ .voice-overlay.compact .voice-overlay-stage {
444
+ gap: 12px;
445
+ }
446
+ .voice-overlay.compact .voice-overlay-center {
447
+ gap: 12px;
448
+ }
449
+ .voice-overlay.compact .voice-orb-container {
450
+ width: 138px;
451
+ height: 138px;
452
+ }
453
+ .voice-overlay.compact .voice-transcript-area {
454
+ max-width: none;
455
+ width: 100%;
456
+ min-height: 46px;
457
+ }
458
+ .voice-overlay.compact .voice-transcript-user {
459
+ font-size: 14px;
460
+ }
461
+ .voice-overlay.compact .voice-transcript-assistant {
462
+ font-size: 15px;
463
+ line-height: 1.4;
464
+ }
465
+ .voice-overlay.compact .voice-tool-cards {
466
+ max-width: none;
467
+ }
468
+ .voice-overlay.compact .voice-overlay-chat {
469
+ width: 100%;
470
+ min-width: 0;
471
+ max-width: none;
472
+ max-height: 44vh;
473
+ }
474
+ .voice-overlay.compact .voice-end-btn {
475
+ width: 56px;
476
+ height: 56px;
477
+ font-size: 20px;
425
478
  }
426
479
  `;
427
480
  document.head.appendChild(style);
@@ -441,6 +494,8 @@ function formatDuration(seconds) {
441
494
  * @param {{
442
495
  * visible: boolean,
443
496
  * onClose: () => void,
497
+ * onDismiss?: () => void,
498
+ * compact?: boolean,
444
499
  * tier: number,
445
500
  * sessionId?: string,
446
501
  * executor?: string,
@@ -453,6 +508,8 @@ function formatDuration(seconds) {
453
508
  export function VoiceOverlay({
454
509
  visible,
455
510
  onClose,
511
+ onDismiss,
512
+ compact = false,
456
513
  tier = 1,
457
514
  sessionId,
458
515
  executor,
@@ -470,16 +527,33 @@ export function VoiceOverlay({
470
527
  const [meetingChatError, setMeetingChatError] = useState(null);
471
528
  const autoVisionAppliedRef = useRef(false);
472
529
  const meetingScrollRef = useRef(null);
530
+ const [usingSdk, setUsingSdk] = useState(false);
531
+ const sdkFallbackCleanupRef = useRef(null);
473
532
 
474
533
  useEffect(() => { injectOverlayStyles(); }, []);
475
534
 
476
- // Use computed signals based on tier
477
- const state = tier === 1 ? voiceState.value : fallbackState.value;
478
- const transcript = tier === 1 ? voiceTranscript.value : fallbackTranscript.value;
479
- const response = tier === 1 ? voiceResponse.value : fallbackResponse.value;
480
- const error = tier === 1 ? voiceError.value : fallbackError.value;
481
- const toolCalls = tier === 1 ? voiceToolCalls.value : [];
482
- const duration = tier === 1 ? voiceDuration.value : 0;
535
+ // Determine effective tier: SDK takes over tier 1 when active
536
+ const effectiveSdk = usingSdk && sdkVoiceSdkActive.value;
537
+
538
+ // Use computed signals based on tier SDK overrides tier 1 when active
539
+ const state = effectiveSdk
540
+ ? sdkVoiceState.value
541
+ : tier === 1 ? voiceState.value : fallbackState.value;
542
+ const transcript = effectiveSdk
543
+ ? sdkVoiceTranscript.value
544
+ : tier === 1 ? voiceTranscript.value : fallbackTranscript.value;
545
+ const response = effectiveSdk
546
+ ? sdkVoiceResponse.value
547
+ : tier === 1 ? voiceResponse.value : fallbackResponse.value;
548
+ const error = effectiveSdk
549
+ ? sdkVoiceError.value
550
+ : tier === 1 ? voiceError.value : fallbackError.value;
551
+ const toolCalls = effectiveSdk
552
+ ? sdkVoiceToolCalls.value
553
+ : tier === 1 ? voiceToolCalls.value : [];
554
+ const duration = effectiveSdk
555
+ ? sdkVoiceDuration.value
556
+ : tier === 1 ? voiceDuration.value : 0;
483
557
  const visionState = visionShareState.value;
484
558
  const visionSource = visionShareSource.value;
485
559
  const visionErr = visionShareError.value;
@@ -495,12 +569,35 @@ export function VoiceOverlay({
495
569
  return normalizedCallType === "video" ? "camera" : null;
496
570
  })();
497
571
 
498
- // Start session on mount
572
+ // Start session on mount — try Agents SDK first, fallback to legacy
499
573
  useEffect(() => {
500
574
  if (!visible || started) return;
501
575
  setStarted(true);
576
+
502
577
  if (tier === 1) {
503
- startVoiceSession({ sessionId, executor, mode, model });
578
+ // Try SDK-first for tier 1
579
+ startSdkVoiceSession({ sessionId, executor, mode, model })
580
+ .then((result) => {
581
+ if (result.sdk) {
582
+ setUsingSdk(true);
583
+ } else {
584
+ // SDK not available — fallback to legacy WebRTC
585
+ setUsingSdk(false);
586
+ startVoiceSession({ sessionId, executor, mode, model });
587
+ }
588
+ })
589
+ .catch(() => {
590
+ // SDK threw unexpectedly — fallback to legacy
591
+ setUsingSdk(false);
592
+ startVoiceSession({ sessionId, executor, mode, model });
593
+ });
594
+
595
+ // Listen for SDK runtime failures to auto-fallback
596
+ const cleanup = onSdkVoiceEvent("sdk-unavailable", () => {
597
+ setUsingSdk(false);
598
+ startVoiceSession({ sessionId, executor, mode, model });
599
+ });
600
+ sdkFallbackCleanupRef.current = cleanup;
504
601
  } else if (sessionId) {
505
602
  startFallbackSession(sessionId, { executor, mode, model });
506
603
  }
@@ -600,23 +697,39 @@ export function VoiceOverlay({
600
697
  const handleClose = useCallback(() => {
601
698
  haptic("medium");
602
699
  stopVisionShare().catch(() => {});
603
- if (tier === 1) {
700
+ // Clean up SDK fallback listener
701
+ if (typeof sdkFallbackCleanupRef.current === "function") {
702
+ sdkFallbackCleanupRef.current();
703
+ sdkFallbackCleanupRef.current = null;
704
+ }
705
+ if (usingSdk) {
706
+ stopSdkVoiceSession();
707
+ } else if (tier === 1) {
604
708
  stopVoiceSession();
605
709
  } else {
606
710
  stopFallbackSession();
607
711
  }
712
+ setUsingSdk(false);
608
713
  setStarted(false);
609
714
  onClose();
610
- }, [tier, onClose]);
715
+ }, [tier, onClose, usingSdk]);
716
+
717
+ const handleDismiss = useCallback(() => {
718
+ haptic("light");
719
+ const fn = typeof onDismiss === "function" ? onDismiss : onClose;
720
+ fn();
721
+ }, [onDismiss, onClose]);
611
722
 
612
723
  const handleInterrupt = useCallback(() => {
613
724
  haptic("light");
614
- if (tier === 1) {
725
+ if (usingSdk) {
726
+ interruptSdkResponse();
727
+ } else if (tier === 1) {
615
728
  interruptResponse();
616
729
  } else {
617
730
  interruptFallback();
618
731
  }
619
- }, [tier]);
732
+ }, [tier, usingSdk]);
620
733
 
621
734
  const handleToggleScreenShare = useCallback(() => {
622
735
  haptic("light");
@@ -689,10 +802,10 @@ export function VoiceOverlay({
689
802
  : "live";
690
803
 
691
804
  return html`
692
- <div class="voice-overlay">
805
+ <div class=${`voice-overlay${compact ? " compact" : ""}`}>
693
806
  <!-- Header -->
694
807
  <div class="voice-overlay-header">
695
- <button class="voice-overlay-close" onClick=${handleClose} title="End voice session">
808
+ <button class="voice-overlay-close" onClick=${handleDismiss} title="Hide voice window">
696
809
  ${resolveIcon("close")}
697
810
  </button>
698
811
  <div>
@@ -38,8 +38,8 @@ function injectVoiceStyles() {
38
38
  display: inline-flex;
39
39
  align-items: center;
40
40
  justify-content: center;
41
- width: 32px;
42
- height: 32px;
41
+ width: 34px;
42
+ height: 34px;
43
43
  border-radius: 50%;
44
44
  border: 1px solid rgba(255,255,255,0.10);
45
45
  background: var(--tg-theme-secondary-bg-color, #1e1e2e);
@@ -49,10 +49,16 @@ function injectVoiceStyles() {
49
49
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
50
50
  -webkit-tap-highlight-color: transparent;
51
51
  padding: 0;
52
- font-size: 15px;
52
+ box-sizing: border-box;
53
+ font-size: 0;
53
54
  line-height: 1;
54
55
  user-select: none;
55
56
  }
57
+ .mic-btn svg {
58
+ width: 16px;
59
+ height: 16px;
60
+ display: block;
61
+ }
56
62
  .mic-btn:hover:not(:disabled) {
57
63
  background: rgba(255,255,255,0.06);
58
64
  color: var(--tg-theme-text-color, #fff);
@@ -74,9 +80,12 @@ function injectVoiceStyles() {
74
80
  50% { box-shadow: 0 0 0 6px rgba(239,68,68,0.06); }
75
81
  }
76
82
  .mic-btn-sm {
77
- width: 26px;
78
- height: 26px;
79
- font-size: 13px;
83
+ width: 24px;
84
+ height: 24px;
85
+ }
86
+ .mic-btn-sm svg {
87
+ width: 12px;
88
+ height: 12px;
80
89
  }
81
90
  .mic-btn-inline {
82
91
  position: absolute;