import type { Agent } from "@/stores/session-store"; import type { WorkspaceTabSnapshot } from "@/stores/workspace-layout-actions"; import { shouldAutoOpenAgentTab } from "@/subagents/policies"; import { normalizeWorkspacePath } from "@/utils/workspace-identity"; function normalizeWorkspaceId(value: string | null | undefined): string { return normalizeWorkspacePath(value) ?? ""; } export interface WorkspaceAgentVisibility { activeAgentIds: Set; autoOpenAgentIds: Set; knownAgentIds: Set; } export function deriveWorkspaceAgentVisibility(input: { sessionAgents: Map | undefined; agentDetails?: Map | undefined; workspaceDirectory: string | null | undefined; }): WorkspaceAgentVisibility { const { sessionAgents, agentDetails, workspaceDirectory } = input; const normalizedWorkspaceDirectory = normalizeWorkspaceId(workspaceDirectory); if ((!sessionAgents && !agentDetails) || !normalizedWorkspaceDirectory) { return { activeAgentIds: new Set(), autoOpenAgentIds: new Set(), knownAgentIds: new Set(), }; } const activeAgentIds = new Set(); const autoOpenAgentIds = new Set(); const knownAgentIds = new Set(); for (const agent of sessionAgents?.values() ?? []) { if (normalizeWorkspaceId(agent.cwd) !== normalizedWorkspaceDirectory) { continue; } knownAgentIds.add(agent.id); if (!agent.archivedAt) { activeAgentIds.add(agent.id); if (shouldAutoOpenAgentTab(agent)) { autoOpenAgentIds.add(agent.id); } } } for (const agent of agentDetails?.values() ?? []) { if (normalizeWorkspaceId(agent.cwd) !== normalizedWorkspaceDirectory) { continue; } knownAgentIds.add(agent.id); } return { activeAgentIds, autoOpenAgentIds, knownAgentIds }; } export function buildWorkspaceTabSnapshot(input: { agentVisibility: WorkspaceAgentVisibility; agentsHydrated: boolean; terminalsHydrated: boolean; knownTerminalIds: Iterable; standaloneTerminalIds: Iterable; hasActivePendingDraftCreate: boolean; }): WorkspaceTabSnapshot { return { agentsHydrated: input.agentsHydrated, terminalsHydrated: input.terminalsHydrated, activeAgentIds: input.agentVisibility.activeAgentIds, autoOpenAgentIds: input.agentVisibility.autoOpenAgentIds, knownAgentIds: input.agentVisibility.knownAgentIds, knownTerminalIds: input.knownTerminalIds, standaloneTerminalIds: input.standaloneTerminalIds, hasActivePendingDraftCreate: input.hasActivePendingDraftCreate, }; } export function workspaceAgentVisibilityEqual( a: WorkspaceAgentVisibility, b: WorkspaceAgentVisibility, ): boolean { return ( setsEqual(a.activeAgentIds, b.activeAgentIds) && setsEqual(a.autoOpenAgentIds, b.autoOpenAgentIds) && setsEqual(a.knownAgentIds, b.knownAgentIds) ); } function setsEqual(a: Set, b: Set): boolean { if (a.size !== b.size) { return false; } for (const item of a) { if (!b.has(item)) { return false; } } return true; } // Prune agent tabs that are no longer active once agents are hydrated. // Archived agents get pruned so that archiving on one client closes the tab on all clients. export function shouldPruneWorkspaceAgentTab(input: { agentId: string; agentsHydrated: boolean; activeAgentIds: Set; }): boolean { if (!input.agentId.trim()) { return false; } if (!input.agentsHydrated) { return false; } return !input.activeAgentIds.has(input.agentId); }