From 3f0d9f6ef8a200b84de003fe5dbd3d0ed95a5fec Mon Sep 17 00:00:00 2001 From: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Date: Wed, 10 Jun 2026 02:01:37 +0530 Subject: [PATCH] feat(meetings): respondTo required + wake-phrase filtering UI (#3555) --- app/src/components/skills/MeetingBotsCard.tsx | 44 +- .../skills/__tests__/MeetingBotsCard.test.tsx | 17 +- app/src/lib/i18n/ar.ts | 4 +- app/src/lib/i18n/bn.ts | 4 +- app/src/lib/i18n/de.ts | 4 +- app/src/lib/i18n/en.ts | 4 +- app/src/lib/i18n/es.ts | 4 +- app/src/lib/i18n/fr.ts | 4 +- app/src/lib/i18n/hi.ts | 4 +- app/src/lib/i18n/id.ts | 4 +- app/src/lib/i18n/it.ts | 4 +- app/src/lib/i18n/ko.ts | 4 +- app/src/lib/i18n/pl.ts | 4 +- app/src/lib/i18n/pt.ts | 4 +- app/src/lib/i18n/ru.ts | 4 +- app/src/lib/i18n/zh-CN.ts | 5 +- app/src/pages/Conversations.tsx | 10 +- .../conversations/utils/threadFilter.test.ts | 43 ++ .../pages/conversations/utils/threadFilter.ts | 11 +- app/src/services/meetCallService.ts | 6 + app/src/services/socketService.ts | 34 +- app/src/store/backendMeetSlice.ts | 30 +- .../specs/gmeet-connections-tab.spec.ts | 2 +- src/core/event_bus/events.rs | 47 +- src/core/jsonrpc.rs | 2 + src/core/socketio.rs | 29 +- src/openhuman/agent_meetings/bus.rs | 139 +++++ src/openhuman/agent_meetings/calendar.rs | 549 ++++++++++++++++++ src/openhuman/agent_meetings/mod.rs | 2 + src/openhuman/agent_meetings/ops.rs | 202 ++++++- src/openhuman/agent_meetings/schemas.rs | 55 +- src/openhuman/agent_meetings/types.rs | 12 +- src/openhuman/channels/runtime/startup.rs | 2 + src/openhuman/socket/event_handlers.rs | 105 +++- 34 files changed, 1316 insertions(+), 82 deletions(-) create mode 100644 src/openhuman/agent_meetings/bus.rs create mode 100644 src/openhuman/agent_meetings/calendar.rs diff --git a/app/src/components/skills/MeetingBotsCard.tsx b/app/src/components/skills/MeetingBotsCard.tsx index ed5243c53..861738232 100644 --- a/app/src/components/skills/MeetingBotsCard.tsx +++ b/app/src/components/skills/MeetingBotsCard.tsx @@ -21,6 +21,7 @@ import { resetBackendMeet, selectBackendMeetLastHarness, selectBackendMeetLastReply, + selectBackendMeetListenOnly, selectBackendMeetStatus, selectBackendMeetUrl, setBackendMeetJoining, @@ -83,6 +84,7 @@ function ActiveMeetingView({ onToast }: Props) { const dispatch = useAppDispatch(); const status = useAppSelector(selectBackendMeetStatus); const meetUrl = useAppSelector(selectBackendMeetUrl); + const listenOnly = useAppSelector(selectBackendMeetListenOnly); const lastReply = useAppSelector(selectBackendMeetLastReply); const lastHarness = useAppSelector(selectBackendMeetLastHarness); const face = faceFromMeetState(status, lastReply, lastHarness); @@ -114,14 +116,18 @@ function ActiveMeetingView({ onToast }: Props) { } }; - const statusText = - { + const statusText = (() => { + const base: Record = { joining: t('skills.meetingBots.liveStatusJoining'), - active: t('skills.meetingBots.liveStatusActive'), + active: listenOnly + ? t('skills.meetingBots.liveStatusListening') + : t('skills.meetingBots.liveStatusActive'), ended: t('skills.meetingBots.liveStatusEnded'), error: t('skills.meetingBots.liveStatusError'), idle: '', - }[status] ?? ''; + }; + return base[status] ?? ''; + })(); const canLeave = status === 'active' || status === 'joining'; const isDone = status === 'ended' || status === 'error'; @@ -242,6 +248,7 @@ export function MeetingBotsModal({ onClose, onToast }: ModalProps) { const { t } = useT(); const dispatch = useAppDispatch(); const [meetUrl, setMeetUrl] = useState(''); + const [respondTo, setRespondTo] = useState(''); const personaDisplayName = useAppSelector(selectPersonaDisplayName); const personaDescription = useAppSelector(selectPersonaDescription); const selectedMascotId = useAppSelector(selectSelectedMascotId); @@ -299,9 +306,12 @@ export function MeetingBotsModal({ onClose, onToast }: ModalProps) { setError(null); setSubmitting(true); try { + // Generate a correlation ID so every backend event for this session + // can be tied back to this meeting. + const meetingId = crypto.randomUUID(); // Optimistically update Redux state so the banner transitions to // the ActiveMeetingView immediately, before the backend responds. - dispatch(setBackendMeetJoining({ meetUrl: meetUrl.trim() })); + dispatch(setBackendMeetJoining({ meetUrl: meetUrl.trim(), meetingId })); // Backend Recall.ai bot: sends the mascot into the meeting via // the backend's Recall.ai integration. The backend joins as a // participant, renders the mascot as the bot's camera feed, and @@ -314,6 +324,8 @@ export function MeetingBotsModal({ onClose, onToast }: ModalProps) { systemPrompt, mascotId, riveColors, + correlationId: meetingId, + respondToParticipant: respondTo.trim() || undefined, }); onToast?.({ type: 'success', @@ -380,6 +392,26 @@ export function MeetingBotsModal({ onClose, onToast }: ModalProps) { /> + + {error && (