From 6b5895dcfef18beb99543ced03cfbbc6dd42abc8 Mon Sep 17 00:00:00 2001 From: Luke The Dev <252071647+iamlukethedev@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:17:30 -0500 Subject: [PATCH] Feature/avatar customization (#25) * Avatar Customization + Update Agent Brain * Bugfixes --------- Co-authored-by: iamlukethedev --- .../components/AgentAvatarPreview3D.tsx | 4 +++ src/features/office/screens/OfficeScreen.tsx | 30 ++++++++++++++----- src/features/retro-office/RetroOffice3D.tsx | 20 ++++++++++--- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/features/agents/components/AgentAvatarPreview3D.tsx b/src/features/agents/components/AgentAvatarPreview3D.tsx index 037cce5..70727d7 100644 --- a/src/features/agents/components/AgentAvatarPreview3D.tsx +++ b/src/features/agents/components/AgentAvatarPreview3D.tsx @@ -19,6 +19,10 @@ const PreviewFigure = ({ const groupRef = useRef(null); const reportedReadyRef = useRef(false); + useEffect(() => { + reportedReadyRef.current = false; + }, [profile]); + useFrame((state) => { if (!reportedReadyRef.current) { reportedReadyRef.current = true; diff --git a/src/features/office/screens/OfficeScreen.tsx b/src/features/office/screens/OfficeScreen.tsx index cbf6473..aace526 100644 --- a/src/features/office/screens/OfficeScreen.tsx +++ b/src/features/office/screens/OfficeScreen.tsx @@ -1799,11 +1799,18 @@ export function OfficeScreen({ spokenPhoneCallKeysRef.current = new Set( [...spokenPhoneCallKeysRef.current].filter((key) => activeKeys.has(key)), ); - setPreparedPhoneCallsByAgentId((previous) => - Object.fromEntries( + setPreparedPhoneCallsByAgentId((previous) => { + const next = Object.fromEntries( Object.entries(previous).filter(([, entry]) => activeKeys.has(entry.requestKey)), - ), - ); + ); + if ( + Object.keys(previous).length === Object.keys(next).length && + Object.keys(previous).every((agentId) => previous[agentId] === next[agentId]) + ) { + return previous; + } + return next; + }); }, [phoneCallByAgentId]); useEffect(() => { @@ -1955,11 +1962,18 @@ export function OfficeScreen({ preparedTextMessageKeysRef.current = new Set( [...preparedTextMessageKeysRef.current].filter((key) => activeKeys.has(key)), ); - setPreparedTextMessagesByAgentId((previous) => - Object.fromEntries( + setPreparedTextMessagesByAgentId((previous) => { + const next = Object.fromEntries( Object.entries(previous).filter(([, entry]) => activeKeys.has(entry.requestKey)), - ), - ); + ); + if ( + Object.keys(previous).length === Object.keys(next).length && + Object.keys(previous).every((agentId) => previous[agentId] === next[agentId]) + ) { + return previous; + } + return next; + }); }, [textMessageByAgentId]); useEffect(() => { diff --git a/src/features/retro-office/RetroOffice3D.tsx b/src/features/retro-office/RetroOffice3D.tsx index 33e19fe..7939d1d 100644 --- a/src/features/retro-office/RetroOffice3D.tsx +++ b/src/features/retro-office/RetroOffice3D.tsx @@ -2133,6 +2133,8 @@ export function RetroOffice3D({ const phoneBoothAgentIdRef = useRef(null); const onPhoneCallSpeakRef = useRef(onPhoneCallSpeak); const onPhoneCallCompleteRef = useRef(onPhoneCallComplete); + const onStandupArrivalsChangeRef = useRef(onStandupArrivalsChange); + const lastStandupArrivalKeyRef = useRef(null); const effectiveSmsBoothAgentIdRef = useRef(null); const effectiveTextMessageScenarioRef = useRef(null); const smsBoothAgentIdRef = useRef(null); @@ -2221,7 +2223,10 @@ export function RetroOffice3D({ useEffect(() => { const intervalId = window.setInterval(() => { const now = Date.now(); - setJanitorActors((previous) => pruneExpiredJanitorActors(previous, now)); + setJanitorActors((previous) => { + const next = pruneExpiredJanitorActors(previous, now); + return next.length === previous.length ? previous : next; + }); }, 1000); return () => { window.clearInterval(intervalId); @@ -2590,6 +2595,7 @@ export function RetroOffice3D({ phoneBoothAgentIdRef.current = phoneBoothAgentId; onPhoneCallSpeakRef.current = onPhoneCallSpeak; onPhoneCallCompleteRef.current = onPhoneCallComplete; + onStandupArrivalsChangeRef.current = onStandupArrivalsChange; }, [ effectiveSmsBoothAgentId, effectiveTextMessageScenario, @@ -2599,6 +2605,7 @@ export function RetroOffice3D({ effectivePhoneCallScenario, onPhoneCallComplete, onPhoneCallSpeak, + onStandupArrivalsChange, phoneBoothAgentId, ]); @@ -3008,7 +3015,10 @@ export function RetroOffice3D({ } if (!standupActive || !standupMeeting) { - onStandupArrivalsChange?.([]); + const nextArrivalsKey = ""; + if (lastStandupArrivalKeyRef.current === nextArrivalsKey) return; + lastStandupArrivalKeyRef.current = nextArrivalsKey; + onStandupArrivalsChangeRef.current?.([]); return; } @@ -3017,7 +3027,10 @@ export function RetroOffice3D({ if (!agent || agent.interactionTarget !== "meeting_room") return false; return Math.hypot(agent.x - agent.targetX, agent.y - agent.targetY) < 18; }); - onStandupArrivalsChange?.(arrivedParticipants); + const nextArrivalsKey = arrivedParticipants.join("|"); + if (lastStandupArrivalKeyRef.current === nextArrivalsKey) return; + lastStandupArrivalKeyRef.current = nextArrivalsKey; + onStandupArrivalsChangeRef.current?.(arrivedParticipants); }; syncArrivalState(); @@ -3029,7 +3042,6 @@ export function RetroOffice3D({ githubReviewAgentId, manualSmsBoothOpen, manualPhoneBoothOpen, - onStandupArrivalsChange, phoneBoothAgentId, qaTestingAgentId, renderAgentLookupRef,