diff --git a/app/src/components/skills/MeetingBotsCard.tsx b/app/src/components/skills/MeetingBotsCard.tsx index b357f0103..6d0d3fa47 100644 --- a/app/src/components/skills/MeetingBotsCard.tsx +++ b/app/src/components/skills/MeetingBotsCard.tsx @@ -202,7 +202,15 @@ function MeetingBotsInline({ onToast, hasSubmittedRef }: MeetingBotsInlineProps) const { t } = useT(); const dispatch = useAppDispatch(); const [meetUrl, setMeetUrl] = useState(''); - const [respondTo] = useState(''); + // The participant the bot answers to (authorized speaker). Wired to the + // backend join payload as `respondToParticipant` → `respondTo`, which the + // meeting stream uses to gate in-call requests to this speaker only. + const [respondTo, setRespondTo] = useState(''); + // Active (respond when addressed) vs listen-only (transcribe only). Defaults + // to active; the bot still only replies after being addressed by the wake + // phrase. Forwarded to the backend as `listenOnly` and mirrored into the + // meet slice so the active view shows the right status. + const [listenOnly, setListenOnly] = useState(false); const personaDisplayName = useAppSelector(selectPersonaDisplayName); const personaDescription = useAppSelector(selectPersonaDescription); const selectedMascotId = useAppSelector(selectSelectedMascotId); @@ -234,7 +242,7 @@ function MeetingBotsInline({ onToast, hasSubmittedRef }: MeetingBotsInlineProps) }, [refreshRecentCalls]); const selectedLabel = t('skills.meetingBots.platforms.gmeet'); - const agentName = personaDisplayName.trim() || 'OpenHuman'; + const agentName = personaDisplayName.trim() || 'Tiny'; const systemPrompt = personaDescription.trim() || undefined; const mascotId = selectedMascotId ?? (mascotColor === 'custom' ? undefined : mascotColor); const riveColors = @@ -264,7 +272,7 @@ function MeetingBotsInline({ onToast, hasSubmittedRef }: MeetingBotsInlineProps) hasSubmittedRef.current = true; try { const meetingId = crypto.randomUUID(); - dispatch(setBackendMeetJoining({ meetUrl: meetUrl.trim(), meetingId })); + dispatch(setBackendMeetJoining({ meetUrl: meetUrl.trim(), meetingId, listenOnly })); await joinMeetViaBackendBot({ meetUrl, displayName: agentName, @@ -275,6 +283,7 @@ function MeetingBotsInline({ onToast, hasSubmittedRef }: MeetingBotsInlineProps) riveColors, correlationId: meetingId, respondToParticipant: respondTo.trim() || undefined, + listenOnly, }); } catch (err) { const message = err instanceof Error ? err.message : t('skills.meetingBots.failedToStart'); @@ -315,6 +324,44 @@ function MeetingBotsInline({ onToast, hasSubmittedRef }: MeetingBotsInlineProps) /> + + + + {error && (