From ba453f91a825f4654f16b1b7b387b8cd991c73b2 Mon Sep 17 00:00:00 2001 From: Robin Braumann Date: Tue, 12 May 2026 08:05:52 +0200 Subject: [PATCH] fix(agents): dedupe profile loads by key --- .../agents/__tests__/AgentMemoryPanel.test.ts | 37 +++++++++++++++++++ .../agents/components/AgentMemoryPanel.vue | 22 +++++++---- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/packages/frontend/editor-ui/src/features/agents/__tests__/AgentMemoryPanel.test.ts b/packages/frontend/editor-ui/src/features/agents/__tests__/AgentMemoryPanel.test.ts index aaca966a41e..adb29c29ed1 100644 --- a/packages/frontend/editor-ui/src/features/agents/__tests__/AgentMemoryPanel.test.ts +++ b/packages/frontend/editor-ui/src/features/agents/__tests__/AgentMemoryPanel.test.ts @@ -310,4 +310,41 @@ describe('AgentMemoryPanel', () => { 'Profile for the second agent.', ); }); + + it('does not start a duplicate profile request when switching back to an in-flight agent id', async () => { + const firstLoad = deferred<{ userProfile: string | null }>(); + const secondLoad = deferred<{ userProfile: string | null }>(); + getAgentMemoryProfilesMock + .mockReturnValueOnce(firstLoad.promise) + .mockReturnValueOnce(secondLoad.promise); + const wrapper = mountPanel(); + + await wrapper.find('[data-testid="agent-memory-profiles-toggle"]').trigger('click'); + await wrapper.setProps({ agentId: 'agent-2' }); + await wrapper.setProps({ agentId: 'agent-1' }); + + expect(getAgentMemoryProfilesMock).toHaveBeenCalledTimes(2); + expect(getAgentMemoryProfilesMock).toHaveBeenNthCalledWith( + 1, + restApiContext, + 'project-1', + 'agent-1', + ); + expect(getAgentMemoryProfilesMock).toHaveBeenNthCalledWith( + 2, + restApiContext, + 'project-1', + 'agent-2', + ); + + secondLoad.resolve({ userProfile: 'Stale second profile.' }); + await flushPromises(); + expect(wrapper.text()).toContain('Loading user profile...'); + + firstLoad.resolve({ userProfile: 'Profile for the first agent.' }); + await flushPromises(); + expect(wrapper.find('[data-testid="agent-memory-user-profile"]').text()).toContain( + 'Profile for the first agent.', + ); + }); }); diff --git a/packages/frontend/editor-ui/src/features/agents/components/AgentMemoryPanel.vue b/packages/frontend/editor-ui/src/features/agents/components/AgentMemoryPanel.vue index 0925a60db52..8a1c2a28611 100644 --- a/packages/frontend/editor-ui/src/features/agents/components/AgentMemoryPanel.vue +++ b/packages/frontend/editor-ui/src/features/agents/components/AgentMemoryPanel.vue @@ -30,11 +30,13 @@ const profilesKey = computed(() => ); const profilesExpanded = ref(false); const loadedProfilesKey = ref(null); -const loadingProfilesKey = ref(null); +const loadingProfilesKeys = ref(new Set()); const profilesError = ref(false); const profiles = ref(null); const profilesLoaded = computed(() => loadedProfilesKey.value === profilesKey.value); -const profilesLoading = computed(() => loadingProfilesKey.value === profilesKey.value); +const profilesLoading = computed( + () => profilesKey.value !== null && loadingProfilesKeys.value.has(profilesKey.value), +); function onEnableMemory() { const existingMemory = props.config?.memory; @@ -64,17 +66,19 @@ function onMemoryToggle(enabled: boolean) { async function loadProfiles() { const key = profilesKey.value; - if (!canLoadProfiles.value || !props.projectId || !props.agentId || !key) return; - if (loadingProfilesKey.value === key) return; + const projectId = props.projectId; + const agentId = props.agentId; + if (!canLoadProfiles.value || !projectId || !agentId || !key) return; + if (loadingProfilesKeys.value.has(key)) return; - loadingProfilesKey.value = key; + loadingProfilesKeys.value = new Set([...loadingProfilesKeys.value, key]); profilesError.value = false; try { const loadedProfiles = await getAgentMemoryProfiles( rootStore.restApiContext, - props.projectId, - props.agentId, + projectId, + agentId, ); if (profilesKey.value !== key) return; profiles.value = loadedProfiles; @@ -83,7 +87,9 @@ async function loadProfiles() { if (profilesKey.value !== key) return; profilesError.value = true; } finally { - if (loadingProfilesKey.value === key) loadingProfilesKey.value = null; + const nextLoadingKeys = new Set(loadingProfilesKeys.value); + nextLoadingKeys.delete(key); + loadingProfilesKeys.value = nextLoadingKeys; } }