From bcc09542b7fddba47521debf97f3a56595434fa8 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 6 Apr 2026 15:52:53 -0400 Subject: [PATCH] fix: respond to request_contact immediately, send confirmation as text The deferred tool response approach caused Gemini to timeout waiting. Now request_contact responds immediately (telling the agent to wait), and the confirm button sends a text message through the live WebSocket to trigger complete_brief. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../configurator/VoiceAgentProvider.tsx | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/components/configurator/VoiceAgentProvider.tsx b/src/components/configurator/VoiceAgentProvider.tsx index 79b0ce8..2b97511 100644 --- a/src/components/configurator/VoiceAgentProvider.tsx +++ b/src/components/configurator/VoiceAgentProvider.tsx @@ -149,7 +149,6 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi const turnCompleteRef = useRef(true); const briefSubmittedRef = useRef(false); const pendingContactRef = useRef(null); - const pendingContactCallIdRef = useRef(''); const reconnectTranscriptRef = useRef([]); const statusRef = useRef('idle'); const wsRef = useRef(null); @@ -217,9 +216,9 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi const contact = { name: contactName, email: contactEmail }; setPendingContact(contact); pendingContactRef.current = contact; - pendingContactCallIdRef.current = callId; - // Don't return a tool response yet — wait for user confirmation via confirmContact() - return '__DEFERRED__'; + // Respond immediately so Gemini doesn't timeout waiting for a tool response. + // The agent is told to wait — user confirmation comes as a text message via confirmContact(). + return JSON.stringify({ success: true, message: 'Contact card is now shown on screen. Wait for the user to review and confirm before calling complete_brief. Do not proceed until you hear confirmation.' }); } if (name === 'complete_brief') { @@ -293,7 +292,6 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi setSelections({}); setPendingContact(null); pendingContactRef.current = null; - pendingContactCallIdRef.current = ''; } setCompletedBrief(null); setCompletedFormData(null); @@ -463,13 +461,9 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi const responses = []; for (const call of calls) { const result = await handleToolCall(call.name, call.args ?? {}, call.id); - if (result !== '__DEFERRED__') { - responses.push({ id: call.id, name: call.name, response: { result } }); - } - } - if (responses.length > 0) { - ws.send(JSON.stringify({ toolResponse: { functionResponses: responses } })); + responses.push({ id: call.id, name: call.name, response: { result } }); } + ws.send(JSON.stringify({ toolResponse: { functionResponses: responses } })); } } }; @@ -543,7 +537,6 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi setAgentAmplitude(0); setCanReconnect(false); reconnectTranscriptRef.current = []; - pendingContactCallIdRef.current = ''; setStatus('idle'); }, []); @@ -558,19 +551,18 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi const confirmContact = useCallback(() => { if (!pendingContactRef.current) return; - // Send confirmation back through WebSocket so the agent knows + // Send a text message to let the agent know the user confirmed their details if (wsRef.current?.readyState === WebSocket.OPEN) { + const { name, email } = pendingContactRef.current; wsRef.current.send(JSON.stringify({ - toolResponse: { - functionResponses: [{ - id: pendingContactCallIdRef.current, - name: 'request_contact', - response: { result: JSON.stringify({ confirmed: true, name: pendingContactRef.current.name, email: pendingContactRef.current.email }) }, - }], + realtimeInput: { + text: `The user has confirmed their contact details on screen. Name: ${name}, Email: ${email}. You may now call complete_brief.`, }, })); + console.log('[VoiceAgent] Contact confirmed, notified agent'); + } else { + console.warn('[VoiceAgent] Cannot confirm contact — WebSocket not open'); } - pendingContactCallIdRef.current = ''; }, []); const reconnect = useCallback(async () => {