From b5daa1fa663ea013e992302219d104bc23f53977 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 15 Nov 2025 12:49:19 +0100 Subject: [PATCH] Filter active agents from resume list --- .../app/src/components/create-agent-modal.tsx | 18 ++++++++++++++++-- plan.md | 4 +++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/app/src/components/create-agent-modal.tsx b/packages/app/src/components/create-agent-modal.tsx index 5e3adf7cd..544366d39 100644 --- a/packages/app/src/components/create-agent-modal.tsx +++ b/packages/app/src/components/create-agent-modal.tsx @@ -98,7 +98,7 @@ export function CreateAgentModal({ const isCompactLayout = screenWidth < 720; const { recentPaths, addRecentPath } = useRecentPaths(); - const { ws, createAgent, resumeAgent } = useSession(); + const { ws, createAgent, resumeAgent, agents } = useSession(); const router = useRouter(); const [isMounted, setIsMounted] = useState(isVisible); @@ -143,10 +143,24 @@ export function CreateAgentModal({ ); const agentDefinition = providerDefinitionMap.get(selectedProvider); const modeOptions = agentDefinition?.modes ?? []; + const activeSessionIds = useMemo(() => { + const ids = new Set(); + agents.forEach((agent) => { + if (agent.sessionId) { + ids.add(agent.sessionId); + } + const persistedSessionId = agent.persistence?.sessionId; + if (persistedSessionId) { + ids.add(persistedSessionId); + } + }); + return ids; + }, [agents]); const filteredResumeCandidates = useMemo(() => { const providerFilter = resumeProviderFilter; const query = resumeSearchQuery.trim().toLowerCase(); return resumeCandidates + .filter((candidate) => !activeSessionIds.has(candidate.sessionId)) .filter((candidate) => providerFilter === "all" || candidate.provider === providerFilter) .filter((candidate) => { if (query.length === 0) { @@ -157,7 +171,7 @@ export function CreateAgentModal({ return titleText.includes(query) || cwdText.includes(query); }) .sort((a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime()); - }, [resumeCandidates, resumeProviderFilter, resumeSearchQuery]); + }, [activeSessionIds, resumeCandidates, resumeProviderFilter, resumeSearchQuery]); useEffect(() => { if (!agentDefinition) { diff --git a/plan.md b/plan.md index 3e895ef6c..f45e278ff 100644 --- a/plan.md +++ b/plan.md @@ -15,6 +15,8 @@ - Normalized `todo` timeline entries into a dedicated `todo_list` stream item, rendered them with a new plan card in the agent stream (provider badge, completion status, checkboxes), and added consolidation logic plus regression coverage in `test-idempotent-stream.ts`. Ran `npm run typecheck --workspace=@voice-dev/app`. - [x] Change "Refresh from disk" to "Refresh" in the agent three dot menu - Updated the agent overflow menu label in `packages/app/src/app/agent/[id].tsx` so the refresh action now matches the desired wording while keeping the busy state text untouched; no additional changes were required. -- [ ] Are we filtering our own shats (already present in agents storage) from the resume agent list? We should if not. +- [x] Are we filtering our own shats (already present in agents storage) from the resume agent list? We should if not. + - Resume tab now filters out persisted sessions whose session ids match any active agent (live session id or persisted handle) to avoid duplicate entries; verified via `npm run typecheck --workspace=@voice-dev/app`. - [ ] Getting "two children with the same key" for "thoughts" and "assistant" review our keying strategy, and make it more robust and performant, and stable. - [ ] Hydrated session show previous tool calls as loading. At least for claude we're not loading the output Chekc Codex too. +- [ ] Add agent type indicator in the agent list, so we can quickly identify the agent type (Claude, Codex, etc.). On the left of the status pill.