@burdenoff/microfe-vibecontrols 2026.528.2 → 2026.528.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"SessionsPage.js","names":[],"sources":["../../src/pages/SessionsPage.tsx"],"sourcesContent":["import { useState, useMemo, useRef, useCallback, useEffect } from 'react';\nimport {\n Plus,\n Terminal,\n Grid,\n List,\n Play,\n Square,\n RefreshCw,\n Monitor,\n Trash2,\n RotateCcw,\n CheckSquare,\n XSquare,\n Activity,\n Download,\n} from 'lucide-react';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { SessionCard, SessionList, TerminalPanel, SessionShareDialog } from '@/components/sessions';\nimport type { Session, TerminalTab } from '@/components/sessions';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n AgentSetupBanner,\n SearchableSelect,\n SessionNotifications,\n GetHelpButton,\n ConfirmDialog,\n} from '@/components/shared';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport {\n useListSessionsQuery,\n useVibecontrolsAgentHealthProbesLazyQuery,\n useVibecontrolsAgentHealthProbeStreamSubscription,\n useStartSessionMutation,\n useStopSessionMutation,\n useRestartSessionMutation,\n useCreateSessionMutation,\n useStartSessionTerminalMutation,\n useStopSessionTerminalMutation,\n useRestartSessionTerminalMutation,\n useUpdateSessionMutation,\n useTerminateSessionMutation,\n useCheckSessionHealthMutation,\n useSessionStatusChangedSubscription,\n useBulkStopSessionsMutation,\n useBulkRestartSessionsMutation,\n useBulkTerminateSessionsMutation,\n useAgentSessionOrphansLazyQuery,\n useAdoptAgentSessionMutation,\n useExportSessionsMutation,\n} from '@/generated/wspace-operations';\nimport { PermissionButton, AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { SaveToFilesButton } from '@burdenoff/fe-libs/storage';\nimport { useSearchParams } from 'react-router-dom';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n getQuotaDialogContent,\n markQuotaHandledLocally,\n isQuotaExhaustedError,\n} from '@/utils/quotaUtils';\n\ntype ViewMode = 'terminal' | 'grid' | 'list';\n\n// Long-tail health-check fallback. The primary path is the\n// SessionStatusChanged subscription (sub-second updates). This poll only\n// fires if the subscription is dropped; 5m is fine because the server\n// also runs a throttled background health check on every ListSessions\n// query.\nconst HEALTH_CHECK_INTERVAL_MS = 300_000;\n\nfunction normalizeSessionTerminalUrl(\n terminalUrl: string | null | undefined,\n tunnelUrl: string | null | undefined\n): string | null {\n if (!terminalUrl) return null;\n\n try {\n const parsedTerminalUrl = new URL(terminalUrl);\n const blockedHosts = new Set(['localhost', '127.0.0.1', 'host.docker.internal']);\n\n if (tunnelUrl && blockedHosts.has(parsedTerminalUrl.hostname)) {\n const parsedTunnelUrl = new URL(tunnelUrl);\n parsedTunnelUrl.pathname = parsedTerminalUrl.pathname;\n parsedTunnelUrl.search = parsedTerminalUrl.search;\n parsedTunnelUrl.hash = parsedTerminalUrl.hash;\n return parsedTunnelUrl.toString();\n }\n\n return parsedTerminalUrl.toString();\n } catch {\n return terminalUrl;\n }\n}\n\nfunction deriveBrowserSessionStatus(\n status: Session['status'],\n rawTerminalUrl: string | null | undefined,\n normalizedTerminalUrl: string | null,\n agentIsActive?: boolean\n): Session['status'] {\n if (status !== 'RUNNING') return status;\n if (agentIsActive === false) return 'ERROR';\n if (!rawTerminalUrl) return status;\n if (!normalizedTerminalUrl) return 'ERROR';\n\n try {\n const pageProtocol = typeof window !== 'undefined' ? window.location.protocol : 'https:';\n const terminalProtocol = new URL(normalizedTerminalUrl).protocol;\n if (pageProtocol === 'https:' && terminalProtocol !== 'https:') {\n return 'ERROR';\n }\n } catch {\n return 'ERROR';\n }\n\n return status;\n}\n\n/**\n * Sessions management page with terminal-first UI.\n * Persists tab state, supports bulk operations, keyboard shortcuts, and health polling.\n */\nexport function SessionsPage() {\n const { t } = useI18n();\n const { onSessionStart, workspaceId } = useVibeControls();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const [searchParams, setSearchParams] = useSearchParams();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const statusOptions = useMemo(\n () => [\n { value: 'all', label: tr('sessions.statusAll', 'All Statuses') },\n { value: 'RUNNING', label: tr('sessions.running', 'Running') },\n { value: 'STOPPED', label: tr('sessions.stopped', 'Stopped') },\n { value: 'STARTING', label: tr('sessions.starting', 'Starting') },\n { value: 'ERROR', label: tr('common.error', 'Error') },\n ],\n [tr]\n );\n\n const typeOptions = useMemo(\n () => [\n { value: 'all', label: tr('sessions.typeAll', 'All Types') },\n { value: 'TMUX', label: tr('sessions.typeTmux', 'Tmux') },\n { value: 'TERMINAL', label: tr('sessions.typeTerminal', 'Terminal') },\n { value: 'SCRIPT', label: tr('sessions.typeScript', 'Script') },\n { value: 'CUSTOM', label: tr('sessions.typeCustom', 'Custom') },\n ],\n [tr]\n );\n\n // Check permissions\n const canListSessions = hasPermission(VIBE_PERMISSIONS.SESSION_LIST);\n const canCreateSession = hasPermission(VIBE_PERMISSIONS.SESSION_CREATE);\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState('all');\n const [typeFilter, setTypeFilter] = useState('all');\n const [vibeFilter, setVibeFilter] = useState('all');\n const [agentFilter, setAgentFilter] = useState('all');\n const [viewMode, setViewMode] = useState<ViewMode>('terminal');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [createLoading, setCreateLoading] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n // Bulk selection state\n const [selectedSessionIds, setSelectedSessionIds] = useState<Set<string>>(new Set());\n const [bulkLoading, setBulkLoading] = useState(false);\n const [isExporting, setIsExporting] = useState(false);\n\n // Form refs\n const sessionNameRef = useRef<HTMLInputElement>(null);\n const sessionCommandRef = useRef<HTMLInputElement>(null);\n const sessionAutoStartRef = useRef<HTMLInputElement>(null);\n // Advanced: provider-native session name to attach to (tmux/wezterm/zellij).\n // When set on submit, the agent reattaches to an existing daemon-side\n // session instead of spawning a fresh one. Empty = normal create.\n const sessionExternalNameRef = useRef<HTMLInputElement>(null);\n\n // Controlled form state for searchable selects\n const [formVibeId, setFormVibeId] = useState('');\n const [formAgentId, setFormAgentId] = useState('');\n const [formType, setFormType] = useState('TMUX');\n const [formShowAdvanced, setFormShowAdvanced] = useState(false);\n\n // Terminal panel state - use store for persistence across navigation\n const {\n terminalTabs: persistedTabs,\n activeTerminalTabId: persistedActiveTab,\n sessionsViewMode: persistedViewMode,\n setTerminalTabs: setPersistedTabs,\n setActiveTerminalTabId: setPersistedActiveTab,\n setSessionsViewMode: setPersistedViewMode,\n activeSessionId,\n } = useVibeControlsStore();\n\n // Local terminal tabs state (enriched with loading/error/url)\n const [terminalTabs, setTerminalTabs] = useState<TerminalTab[]>([]);\n const [activeTerminalTab, setActiveTerminalTab] = useState<string | null>(null);\n const [terminalCollapsed, setTerminalCollapsed] = useState(false);\n\n // Share dialog state\n const [shareSession, setShareSession] = useState<Session | null>(null);\n\n // Immersive fullscreen state (terminal takes over the entire screen)\n const [isImmersiveFullscreen, setIsImmersiveFullscreen] = useState(false);\n\n // UI state persistence flag — true once we've loaded from store\n const [uiStateLoaded, setUiStateLoaded] = useState(false);\n\n const queryViewMode = searchParams.get('view');\n const querySessionId = searchParams.get('session');\n\n // Mutations\n const [createSession] = useCreateSessionMutation({ refetchQueries: ['ListSessions'] });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_SESSION');\n\n // Reconnect (orphan adoption) modal state. Independent from the\n // New Session form so the user can reconnect without prefilling the\n // create form. Opened via the \"Reconnect\" header button.\n const [showReconnectModal, setShowReconnectModal] = useState(false);\n const [reconnectAgentId, setReconnectAgentId] = useState<string>('');\n const [reconnectError, setReconnectError] = useState<string | null>(null);\n const [reconnectAdoptingName, setReconnectAdoptingName] = useState<string | null>(null);\n const [discoverOrphans, { data: orphansData, loading: orphansLoading }] =\n useAgentSessionOrphansLazyQuery({ fetchPolicy: 'network-only' });\n const [adoptAgentSession] = useAdoptAgentSessionMutation({\n refetchQueries: ['ListSessions'],\n });\n const [startSession] = useStartSessionMutation({ refetchQueries: ['ListSessions'] });\n const [stopSession] = useStopSessionMutation({ refetchQueries: ['ListSessions'] });\n const [restartSession] = useRestartSessionMutation({ refetchQueries: ['ListSessions'] });\n const [startSessionTerminal] = useStartSessionTerminalMutation({\n refetchQueries: ['ListSessions'],\n });\n const [stopSessionTerminal] = useStopSessionTerminalMutation({\n refetchQueries: ['ListSessions'],\n });\n const [restartSessionTerminal] = useRestartSessionTerminalMutation({\n refetchQueries: ['ListSessions'],\n });\n const [updateSession] = useUpdateSessionMutation({ refetchQueries: ['ListSessions'] });\n const [terminateSession] = useTerminateSessionMutation({ refetchQueries: ['ListSessions'] });\n const [checkSessionHealth] = useCheckSessionHealthMutation({ refetchQueries: ['ListSessions'] });\n const [bulkStopSessions] = useBulkStopSessionsMutation({ refetchQueries: ['ListSessions'] });\n const [bulkRestartSessions] = useBulkRestartSessionsMutation({\n refetchQueries: ['ListSessions'],\n });\n const [bulkTerminateSessions] = useBulkTerminateSessionsMutation({\n refetchQueries: ['ListSessions'],\n });\n const [exportSessions] = useExportSessionsMutation();\n\n // ── UI State Persistence (localStorage via Zustand store) ─────────────\n // Load persisted UI state from store on mount\n useEffect(() => {\n if (uiStateLoaded) return;\n\n // Query params take precedence over persisted UI state so session tabs are bookmarkable.\n if (queryViewMode === 'terminal' || queryViewMode === 'grid' || queryViewMode === 'list') {\n setViewMode(queryViewMode);\n } else if (persistedViewMode) {\n setViewMode(persistedViewMode);\n }\n\n if (querySessionId) {\n setActiveTerminalTab(querySessionId);\n } else if (persistedActiveTab) {\n setActiveTerminalTab(persistedActiveTab);\n }\n\n // Restore tabs from store — they'll need to re-fetch terminal URLs\n if (persistedTabs.length > 0) {\n const restoredTabs: TerminalTab[] = persistedTabs.map((t) => ({\n sessionId: t.sessionId,\n name: t.name,\n terminalUrl: null, // Will be populated when sessions load\n loading: false,\n error: null,\n }));\n setTerminalTabs(restoredTabs);\n }\n\n setUiStateLoaded(true);\n }, [\n persistedTabs,\n persistedActiveTab,\n persistedViewMode,\n querySessionId,\n queryViewMode,\n uiStateLoaded,\n ]);\n\n // Once sessions are loaded and we have restored tabs, populate terminal URLs\n const sessionsLoadedRef = useRef(false);\n\n // Sync local state changes to store for persistence\n useEffect(() => {\n if (!uiStateLoaded) return;\n // Sync terminal tabs to store (only sessionId and name for persistence)\n const tabsToStore = terminalTabs.map((t) => ({ sessionId: t.sessionId, name: t.name }));\n setPersistedTabs(tabsToStore);\n }, [terminalTabs, uiStateLoaded, setPersistedTabs]);\n\n useEffect(() => {\n if (!uiStateLoaded) return;\n setPersistedActiveTab(activeTerminalTab);\n }, [activeTerminalTab, uiStateLoaded, setPersistedActiveTab]);\n\n useEffect(() => {\n if (!uiStateLoaded) return;\n setPersistedViewMode(viewMode);\n }, [viewMode, uiStateLoaded, setPersistedViewMode]);\n\n useEffect(() => {\n if (!uiStateLoaded) return;\n\n const next = new URLSearchParams(searchParams);\n let changed = false;\n\n const nextView = viewMode === 'terminal' ? null : viewMode;\n if (nextView) {\n if (next.get('view') !== nextView) {\n next.set('view', nextView);\n changed = true;\n }\n } else if (next.has('view')) {\n next.delete('view');\n changed = true;\n }\n\n if (activeTerminalTab) {\n if (next.get('session') !== activeTerminalTab) {\n next.set('session', activeTerminalTab);\n changed = true;\n }\n } else if (next.has('session')) {\n next.delete('session');\n changed = true;\n }\n\n if (changed) {\n setSearchParams(next, { replace: true });\n }\n }, [activeTerminalTab, searchParams, setSearchParams, uiStateLoaded, viewMode]);\n\n // Track previous activeSessionId to detect changes from the context switcher\n const prevActiveSessionIdRef = useRef<string | null>(null);\n\n // ── Live session-status subscription ─────────────────────────────────\n // Replaces the prior `setInterval(checkSessionHealth, …)` poll. The\n // backend already runs a throttled background health check on every\n // ListSessions query (see SessionService.triggerBackgroundHealthCheck);\n // the subscription propagates resulting status flips into Apollo's\n // normalized cache by id, so any rendered session row updates without\n // a manual refetch.\n //\n // workspaceId comes from VibeControlsProvider; skipping until it\n // resolves avoids a \"Workspace context required\" error during initial\n // mount before the auth token is issued.\n useSessionStatusChangedSubscription({\n skip: !workspaceId,\n variables: { workspaceId: workspaceId || '' },\n });\n\n // Long-tail safety fallback: in case the subscription drops (network\n // blip, agent restart) and a status flip is missed, re-trigger the\n // server-side health check every 5 minutes. Server-side throttling\n // (30s) protects against thundering-herd if multiple tabs are open.\n useEffect(() => {\n const interval = setInterval(() => {\n checkSessionHealth().catch(() => {\n /* silent — fallback path */\n });\n }, HEALTH_CHECK_INTERVAL_MS);\n return () => clearInterval(interval);\n }, [checkSessionHealth]);\n\n // ── Keyboard Shortcuts for Terminal Tabs ────────────────────────────\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Don't intercept when typing in inputs/textareas\n const target = e.target as HTMLElement;\n if (\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.tagName === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n\n const isMod = e.metaKey || e.ctrlKey;\n\n // Ctrl/Cmd+T — New terminal tab (open session picker)\n if (isMod && e.key === 't') {\n e.preventDefault();\n setShowCreateForm(true);\n return;\n }\n\n // Ctrl/Cmd+W — Close active terminal tab\n if (isMod && e.key === 'w' && activeTerminalTab) {\n e.preventDefault();\n const sessionIdToClose = activeTerminalTab;\n setTerminalTabs((prev) => prev.filter((t) => t.sessionId !== sessionIdToClose));\n setActiveTerminalTab(() => {\n const remaining = terminalTabs.filter((t) => t.sessionId !== sessionIdToClose);\n return remaining.length > 0 ? remaining[remaining.length - 1].sessionId : null;\n });\n return;\n }\n\n // Ctrl+Tab — Next terminal tab\n if (e.ctrlKey && e.key === 'Tab' && !e.shiftKey && terminalTabs.length > 1) {\n e.preventDefault();\n const currentIdx = terminalTabs.findIndex((t) => t.sessionId === activeTerminalTab);\n const nextIdx = (currentIdx + 1) % terminalTabs.length;\n setActiveTerminalTab(terminalTabs[nextIdx].sessionId);\n return;\n }\n\n // Ctrl+Shift+Tab — Previous terminal tab\n if (e.ctrlKey && e.key === 'Tab' && e.shiftKey && terminalTabs.length > 1) {\n e.preventDefault();\n const currentIdx = terminalTabs.findIndex((t) => t.sessionId === activeTerminalTab);\n const prevIdx = (currentIdx - 1 + terminalTabs.length) % terminalTabs.length;\n setActiveTerminalTab(terminalTabs[prevIdx].sessionId);\n return;\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [activeTerminalTab, terminalTabs]);\n\n // Fetch sessions from GraphQL API (with filters)\n // Use previousData to prevent flash of loading state during refetches\n // When no filters narrow the result set, the query variables match\n // the InitialData prefetch — use cache-first to skip the redundant\n // network hit. With filters applied, cache-and-network keeps stale\n // narrowed results from being shown silently.\n const noSessionFilters =\n statusFilter === 'all' && typeFilter === 'all' && vibeFilter === 'all' && agentFilter === 'all';\n const sessionsFetchPolicy = noSessionFilters ? 'cache-first' : 'cache-and-network';\n const { data, previousData, loading, error, refetch } = useListSessionsQuery({\n variables: {\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'RUNNING' | 'STOPPED' | 'STARTING' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as 'TMUX' | 'TERMINAL' | 'SSH' | 'SCRIPT' | 'CUSTOM')\n : undefined,\n vibeId: vibeFilter !== 'all' && vibeFilter !== 'none' ? vibeFilter : undefined,\n agentId: agentFilter !== 'all' ? agentFilter : undefined,\n },\n pagination: { limit: 50 },\n },\n fetchPolicy: sessionsFetchPolicy,\n });\n\n // Use current data or fall back to previous data during refetches\n // This prevents the \"Loading sessions...\" flash when refetching\n const effectiveData = data ?? previousData;\n\n // Use cached initial data for dropdowns (agents and vibes)\n // This reads from Apollo cache - no network request\n const { agents: cachedAgents, vibes: cachedVibes } = useCachedInitialData();\n\n // Live deep-health probe per agent (every 30s). Used to gate the\n // \"Create session\" button — without this, the button stays enabled\n // against an agent whose cloudflared tunnel has died, and clicking\n // it produces the user-visible \"Failed to start terminal\" error\n // long after the session row is already created in PENDING state.\n // Initial query primes the cache; the subscription below replaces\n // the prior 30s pollInterval so health flips reach this page in <1s.\n // Probe query is lazy — the subscription below is the source of truth\n // for initial paint, so we only fire the query on user-triggered\n // refresh (see refetch handler below).\n const [refetchProbes, agentHealthQuery] = useVibecontrolsAgentHealthProbesLazyQuery({\n fetchPolicy: 'network-only',\n notifyOnNetworkStatusChange: true,\n });\n const { data: agentHealthStreamData } = useVibecontrolsAgentHealthProbeStreamSubscription();\n const agentHealthByIdRef = useMemo(() => {\n const m = new Map<string, { healthy: boolean; error: string | null }>();\n const probes =\n agentHealthStreamData?.vibecontrolsAgentHealthProbeStream ??\n agentHealthQuery.data?.vibecontrolsAgentHealthProbes ??\n [];\n for (const p of probes) {\n m.set(p.agentId, { healthy: p.healthy, error: p.errorMessage ?? null });\n }\n return m;\n }, [agentHealthQuery.data, agentHealthStreamData]);\n\n // Build vibe options for filter using cached data\n const vibeOptions = useMemo(() => {\n return [\n { value: 'all', label: tr('sessions.filterVibeAll', 'All Vibes') },\n { value: 'none', label: tr('sessions.filterVibeNone', 'No Vibe') },\n ...cachedVibes.map((vibe) => ({\n value: vibe.id,\n label: vibe.name,\n })),\n ];\n }, [cachedVibes, tr]);\n\n // Build agent options for filter using cached data\n const agentOptions = useMemo(() => {\n return [\n { value: 'all', label: tr('sessions.filterAgentAll', 'All Agents') },\n ...cachedAgents.map((agent) => ({\n value: agent.id,\n label: `${agent.name} ${agent.isActive ? '' : `(${tr('sessions.offline', 'Offline')})`}`,\n })),\n ];\n }, [cachedAgents, tr]);\n\n // Build options for create form using cached data (no \"all\"/\"none\" entries)\n const createVibeOptions = useMemo(() => {\n return [\n { value: '', label: tr('sessions.noVibe', 'No vibe') },\n ...cachedVibes.map((vibe) => ({\n value: vibe.id,\n label: vibe.name,\n })),\n ];\n }, [cachedVibes, tr]);\n\n const createAgentOptions = useMemo(() => {\n return [\n { value: '', label: tr('sessions.selectAgent', 'Select an agent') },\n ...cachedAgents.map((agent) => ({\n value: agent.id,\n label: `${agent.name} ${agent.isActive ? `(${tr('sessions.active', 'Active')})` : `(${tr('sessions.offline', 'Offline')})`}`,\n })),\n ];\n }, [cachedAgents, tr]);\n\n const sessionTypeOptions = useMemo(\n () => [\n { value: 'TMUX', label: tr('sessions.typeTmuxLower', 'tmux') },\n { value: 'WEZTERM', label: tr('sessions.typeWezTerm', 'WezTerm') },\n { value: 'ZELLIJ', label: tr('sessions.typeZellij', 'Zellij') },\n { value: 'TERMINAL', label: tr('sessions.typeTerminal', 'Terminal') },\n { value: 'SSH', label: tr('sessions.typeSsh', 'SSH') },\n { value: 'SCRIPT', label: tr('sessions.typeScript', 'Script') },\n { value: 'CUSTOM', label: tr('sessions.typeCustom', 'Custom') },\n ],\n [tr]\n );\n\n // Map GraphQL response to Session type\n // Use effectiveData to prevent flash during refetches\n const sessions: Session[] = useMemo(() => {\n const rawSessions = effectiveData?.sessions?.items ?? [];\n return rawSessions.map((session: NonNullable<typeof rawSessions>[number]) => {\n const normalizedTtydUrl = normalizeSessionTerminalUrl(\n session.ttydUrl || null,\n session.agent?.tunnelUrl || null\n );\n\n return {\n id: session.id,\n workspaceId: session.workspaceId,\n vibeId: session.vibeId || null,\n agentId: session.agentId || null,\n name: session.name,\n type: session.type as Session['type'],\n status: deriveBrowserSessionStatus(\n session.status as Session['status'],\n session.ttydUrl || null,\n normalizedTtydUrl,\n session.agent?.isActive\n ),\n command: session.command || null,\n workingDirectory: session.workingDirectory || null,\n environmentVariables: (session.environmentVariables as Record<string, string>) || {},\n autoStart: session.autoStart,\n providerSessionName: session.providerSessionName || undefined,\n providerWindowName: session.providerWindowId || undefined,\n pid: session.pid || null,\n exitCode: session.exitCode || null,\n lastOutput: session.lastOutput || null,\n ttydUrl: normalizedTtydUrl,\n metadata: (session.metadata as Record<string, unknown>) || {},\n createdAt: session.createdAt,\n updatedAt: session.updatedAt,\n vibe: session.vibe ? { id: session.vibe.id, name: session.vibe.name } : undefined,\n agent: session.agent\n ? {\n id: session.agent.id,\n name: session.agent.name,\n tunnelUrl: session.agent.tunnelUrl || null,\n isActive: session.agent.isActive,\n }\n : undefined,\n };\n });\n }, [effectiveData?.sessions?.items]);\n\n // Populate terminal URLs for restored tabs once sessions load\n useEffect(() => {\n if (sessionsLoadedRef.current || sessions.length === 0 || terminalTabs.length === 0) return;\n sessionsLoadedRef.current = true;\n\n // Re-spawn ttyd for every restored tab. The persisted ttydUrl from\n // the session row is stale on remount: the agent's ttyd process\n // dies on agent restart / idle GC, and the cached URL surfaces as\n // \"Terminal not running for this session\" when the iframe loads.\n // startSessionTerminal is idempotent server-side (re-uses an\n // existing ttyd if it's still running, spawns a fresh one if not),\n // so this is safe to call unconditionally on tab restore. The\n // iframe url returned in the mutation response is the source of\n // truth — we no longer trust the persisted ttydUrl.\n void Promise.allSettled(\n terminalTabs.map(async (tab) => {\n const session = sessions.find((s) => s.id === tab.sessionId);\n if (!session) return;\n try {\n const result = await startSessionTerminal({ variables: { id: tab.sessionId } });\n const tunnelHint = session.agent?.tunnelUrl ?? null;\n const newUrl = normalizeSessionTerminalUrl(\n result.data?.startSessionTerminal?.ttydUrl ?? null,\n tunnelHint\n );\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === tab.sessionId\n ? { ...t, terminalUrl: newUrl, loading: false, error: null }\n : t\n )\n );\n } catch (err) {\n // Don't blow up the tab — leave it loading=false with the\n // cached ttydUrl as a best-effort, the user can hit\n // \"Restart terminal\" if the iframe still 404s.\n // eslint-disable-next-line no-console\n console.warn('[sessions] tab restore startSessionTerminal failed', err);\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === tab.sessionId && session.ttydUrl\n ? { ...t, terminalUrl: session.ttydUrl, loading: false }\n : t\n )\n );\n }\n })\n );\n }, [sessions, terminalTabs, startSessionTerminal]);\n\n // ── Session Switcher Integration ─────────────────────────────────────\n // When activeSessionId changes, open/switch to that terminal\n useEffect(() => {\n // Skip if not initialized or no session selected\n if (!uiStateLoaded || !activeSessionId) {\n prevActiveSessionIdRef.current = activeSessionId;\n return;\n }\n\n // Skip if this is the initial load (not a user-initiated change)\n if (prevActiveSessionIdRef.current === null && activeSessionId) {\n prevActiveSessionIdRef.current = activeSessionId;\n return;\n }\n\n // Skip if the session hasn't actually changed\n if (prevActiveSessionIdRef.current === activeSessionId) {\n return;\n }\n\n prevActiveSessionIdRef.current = activeSessionId;\n\n // Find the session in the sessions list\n const session = sessions.find((s) => s.id === activeSessionId);\n if (!session) return;\n\n // Check if this session already has an open tab\n const existingTab = terminalTabs.find((t) => t.sessionId === activeSessionId);\n\n if (existingTab) {\n // Tab already exists - just switch to it\n setActiveTerminalTab(activeSessionId);\n setTerminalCollapsed(false);\n } else {\n // Need to open a new tab for this session\n const url = session.ttydUrl || null;\n const newTab: TerminalTab = {\n sessionId: session.id,\n name: session.name,\n terminalUrl: url,\n loading: !url,\n error: null,\n };\n setTerminalTabs((prev) => [...prev, newTab]);\n setActiveTerminalTab(activeSessionId);\n setTerminalCollapsed(false);\n\n // If no URL available, start the terminal\n if (!url) {\n startSessionTerminal({ variables: { id: session.id } })\n .then((result) => {\n const newUrl = result.data?.startSessionTerminal?.ttydUrl || null;\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === session.id\n ? {\n ...t,\n terminalUrl: newUrl,\n loading: false,\n error: newUrl\n ? null\n : tr(\n 'sessions.terminalStartedNoUrl',\n 'Terminal started but no URL was returned. Try restarting.'\n ),\n }\n : t\n )\n );\n })\n .catch((err) => {\n const message =\n err instanceof Error\n ? err.message\n : tr('sessions.failedStartTerminal', 'Failed to start terminal');\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === session.id ? { ...t, loading: false, error: message } : t\n )\n );\n });\n }\n }\n }, [activeSessionId, sessions, terminalTabs, uiStateLoaded, startSessionTerminal]);\n\n // Filter sessions\n const filteredSessions = useMemo(() => {\n return sessions.filter((session) => {\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n session.name.toLowerCase().includes(query) ||\n session.command?.toLowerCase().includes(query) ||\n session.workingDirectory?.toLowerCase().includes(query);\n if (!matchesSearch) return false;\n }\n if (vibeFilter === 'none' && session.vibeId) return false;\n return true;\n });\n }, [sessions, searchQuery, vibeFilter]);\n\n // ── Bulk Selection ───────────────────────────────────────────────────\n const toggleSessionSelection = useCallback((sessionId: string) => {\n setSelectedSessionIds((prev) => {\n const next = new Set(prev);\n if (next.has(sessionId)) {\n next.delete(sessionId);\n } else {\n next.add(sessionId);\n }\n return next;\n });\n }, []);\n\n const selectAllSessions = useCallback(() => {\n setSelectedSessionIds(new Set(filteredSessions.map((s) => s.id)));\n }, [filteredSessions]);\n\n const deselectAllSessions = useCallback(() => {\n setSelectedSessionIds(new Set());\n }, []);\n\n const isAllSelected =\n filteredSessions.length > 0 && selectedSessionIds.size === filteredSessions.length;\n const hasSelection = selectedSessionIds.size > 0;\n\n // ── Bulk Actions ─────────────────────────────────────────────────────\n const handleBulkStop = useCallback(async () => {\n if (selectedSessionIds.size === 0) return;\n setBulkLoading(true);\n try {\n const result = await bulkStopSessions({ variables: { ids: Array.from(selectedSessionIds) } });\n const data = result.data?.bulkStopSessions;\n setSuccessMessage(\n `${tr('sessions.stopped', 'Stopped')} ${data?.succeeded ?? 0} ${tr('nav.sessions', 'sessions')}${\n data?.failed ? `, ${data.failed} ${tr('sessions.failed', 'failed')}` : ''\n }`\n );\n setSelectedSessionIds(new Set());\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n setSuccessMessage(\n `${tr('sessions.errorPrefix', 'Error')}: ${\n err instanceof Error ? err.message : tr('sessions.failedStop', 'Failed to stop sessions')\n }`\n );\n setTimeout(() => setSuccessMessage(null), 5000);\n } finally {\n setBulkLoading(false);\n }\n }, [selectedSessionIds, bulkStopSessions, tr]);\n\n const handleBulkRestart = useCallback(async () => {\n if (selectedSessionIds.size === 0) return;\n setBulkLoading(true);\n try {\n const result = await bulkRestartSessions({\n variables: { ids: Array.from(selectedSessionIds) },\n });\n const data = result.data?.bulkRestartSessions;\n setSuccessMessage(\n `${tr('sessions.restarted', 'Restarted')} ${data?.succeeded ?? 0} ${tr('nav.sessions', 'sessions')}${\n data?.failed ? `, ${data.failed} ${tr('sessions.failed', 'failed')}` : ''\n }`\n );\n setSelectedSessionIds(new Set());\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n setSuccessMessage(\n `${tr('sessions.errorPrefix', 'Error')}: ${\n err instanceof Error\n ? err.message\n : tr('sessions.failedRestart', 'Failed to restart sessions')\n }`\n );\n setTimeout(() => setSuccessMessage(null), 5000);\n } finally {\n setBulkLoading(false);\n }\n }, [selectedSessionIds, bulkRestartSessions, tr]);\n\n const handleBulkTerminate = useCallback(async () => {\n if (selectedSessionIds.size === 0) return;\n if (\n !confirm(\n `${tr('sessions.confirmRemovePrefix', 'Are you sure you want to remove')} ${selectedSessionIds.size} ${tr('nav.sessions', 'session(s)')}? ${tr('sessions.confirmRemoveSuffix', 'This cannot be undone.')}`\n )\n )\n return;\n setBulkLoading(true);\n try {\n const result = await bulkTerminateSessions({\n variables: { ids: Array.from(selectedSessionIds) },\n });\n const data = result.data?.bulkTerminateSessions;\n setSuccessMessage(\n `${tr('sessions.removed', 'Removed')} ${data?.succeeded ?? 0} ${tr('nav.sessions', 'sessions')}${\n data?.failed ? `, ${data.failed} ${tr('sessions.failed', 'failed')}` : ''\n }`\n );\n setSelectedSessionIds(new Set());\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n setSuccessMessage(\n `${tr('sessions.errorPrefix', 'Error')}: ${\n err instanceof Error\n ? err.message\n : tr('sessions.failedRemove', 'Failed to remove sessions')\n }`\n );\n setTimeout(() => setSuccessMessage(null), 5000);\n } finally {\n setBulkLoading(false);\n }\n }, [selectedSessionIds, bulkTerminateSessions, tr]);\n\n const handleExportSessions = useCallback(\n async (format: 'CSV' | 'JSON' = 'CSV') => {\n setIsExporting(true);\n try {\n const result = await exportSessions({\n variables: {\n input: {\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'RUNNING' | 'STOPPED' | 'STARTING' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as 'TMUX' | 'TERMINAL' | 'SSH' | 'SCRIPT' | 'CUSTOM')\n : undefined,\n vibeId: vibeFilter !== 'all' && vibeFilter !== 'none' ? vibeFilter : undefined,\n agentId: agentFilter !== 'all' ? agentFilter : undefined,\n },\n format,\n },\n },\n });\n const exportData = result.data?.exportSessions;\n if (!exportData) return;\n\n const mimeType = format === 'CSV' ? 'text/csv' : 'application/json';\n const blob = new Blob([exportData.data], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `sessions-export-${new Date().toISOString().slice(0, 10)}.${format.toLowerCase()}`;\n a.click();\n URL.revokeObjectURL(url);\n setSuccessMessage(\n tr('sessions.exportSuccess', 'Exported {{count}} sessions as {{format}}', {\n count: exportData.count,\n format,\n })\n );\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n setSuccessMessage(\n `${tr('sessions.errorPrefix', 'Error')}: ${\n err instanceof Error\n ? err.message\n : tr('sessions.failedExport', 'Failed to export sessions')\n }`\n );\n setTimeout(() => setSuccessMessage(null), 5000);\n } finally {\n setIsExporting(false);\n }\n },\n [exportSessions, statusFilter, typeFilter, vibeFilter, agentFilter, tr]\n );\n\n // ── Single Session Actions ───────────────────────────────────────────\n const handleSessionSelect = (session: Session) => {\n navigate(`/sessions/${session.id}`);\n };\n\n const handleCreateSession = () => {\n setCreateError(null);\n setShowCreateForm(true);\n };\n\n const handleSubmitCreateSession = async () => {\n const name = sessionNameRef.current?.value?.trim();\n const vibeId = formVibeId;\n const selectedAgentId = formAgentId;\n const type = formType;\n const command = sessionCommandRef.current?.value?.trim();\n const autoStart = sessionAutoStartRef.current?.checked ?? false;\n\n if (!name) {\n setCreateError(tr('sessions.nameRequired', 'Name is required'));\n return;\n }\n\n // Multi-agent: a vibe has 0..N attached agents. Pick the user's selected\n // agent when set; otherwise auto-pick when the vibe has exactly one\n // attached agent; otherwise force an explicit choice.\n const selectedVibe = cachedVibes.find((v) => v.id === vibeId);\n const candidateAgents = selectedVibe?.agents ?? [];\n let agentId: string | null = null;\n if (selectedAgentId && (!vibeId || candidateAgents.some((a) => a.id === selectedAgentId))) {\n agentId = selectedAgentId;\n } else if (candidateAgents.length === 1) {\n agentId = candidateAgents[0]?.id ?? null;\n }\n if (!agentId) {\n const message =\n vibeId && candidateAgents.length === 0\n ? tr(\n 'sessions.noAgentAttached',\n 'This vibe has no agents attached. Attach one from the Vibe → Agents tab before creating sessions.'\n )\n : vibeId && candidateAgents.length > 1\n ? tr(\n 'sessions.pickAgentFromVibe',\n 'This vibe has multiple agents attached. Pick one before creating the session.'\n )\n : tr('sessions.agentRequired', 'Please select an agent for this session.');\n setCreateError(message);\n return;\n }\n\n // Block session create when the agent's live deep-health probe is\n // not green — without this gate, the create succeeds (PENDING row\n // gets written) but the subsequent terminal-start step fails with\n // \"Terminal started but no tunnel URL\". Surface the actual probe\n // error inline instead of letting the user pile up dead PENDING\n // sessions against an unreachable agent.\n const probeRow = agentHealthByIdRef.get(agentId);\n if (probeRow && !probeRow.healthy) {\n setCreateError(\n tr(\n 'sessions.agentNotHealthy',\n 'The selected agent isn\\u2019t fully reachable right now. Please wait for the agent badge to turn green and retry.'\n ) + (probeRow.error ? ` (${probeRow.error})` : '')\n );\n return;\n }\n\n setCreateLoading(true);\n setCreateError(null);\n try {\n const externalSessionName = sessionExternalNameRef.current?.value?.trim() || undefined;\n const result = await createSession({\n variables: {\n input: {\n name,\n type: (type || 'TMUX') as\n | 'TMUX'\n | 'WEZTERM'\n | 'ZELLIJ'\n | 'TERMINAL'\n | 'SSH'\n | 'SCRIPT'\n | 'CUSTOM',\n command: command || undefined,\n vibeId: vibeId || undefined,\n agentId,\n autoStart,\n externalSessionName,\n },\n },\n });\n const newSessionId = result.data?.createSession?.id;\n setShowCreateForm(false);\n setSuccessMessage(tr('sessions.createdSuccess', 'Session created successfully'));\n if (newSessionId) await flushTags(newSessionId);\n setTimeout(() => setSuccessMessage(null), 3000);\n\n if (viewMode === 'terminal' && result.data?.createSession) {\n const newSession: Session = {\n id: result.data.createSession.id,\n workspaceId: result.data.createSession.workspaceId,\n vibeId: result.data.createSession.vibeId || null,\n agentId: result.data.createSession.agentId || null,\n name: result.data.createSession.name,\n type: result.data.createSession.type as Session['type'],\n status: result.data.createSession.status as Session['status'],\n command: result.data.createSession.command || null,\n workingDirectory: result.data.createSession.workingDirectory || null,\n environmentVariables:\n (result.data.createSession.environmentVariables as Record<string, string>) || {},\n autoStart: result.data.createSession.autoStart,\n providerSessionName: result.data.createSession.providerSessionName || undefined,\n providerWindowName: result.data.createSession.providerWindowId || undefined,\n pid: result.data.createSession.pid || null,\n exitCode: result.data.createSession.exitCode || null,\n lastOutput: result.data.createSession.lastOutput || null,\n ttydUrl: result.data.createSession.ttydUrl || null,\n metadata: (result.data.createSession.metadata as Record<string, unknown>) || {},\n createdAt: result.data.createSession.createdAt,\n updatedAt: result.data.createSession.updatedAt,\n };\n handleOpenTerminal(newSession);\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowCreateForm(false);\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n setCreateError(\n err instanceof Error ? err.message : tr('sessions.failedCreate', 'Failed to create session')\n );\n } finally {\n setCreateLoading(false);\n }\n };\n\n const handleStartSession = async (session: Session) => {\n try {\n await startSession({ variables: { id: session.id } });\n onSessionStart?.({\n id: session.id,\n name: session.name,\n type: session.type,\n status: 'RUNNING',\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n console.error('Failed to start session:', err);\n }\n };\n\n const handleStopSession = async (session: Session) => {\n try {\n await stopSession({ variables: { id: session.id } });\n } catch (err) {\n console.error('Failed to stop session:', err);\n }\n };\n\n const handleRestartSession = async (session: Session) => {\n try {\n await restartSession({ variables: { id: session.id } });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n console.error('Failed to restart session:', err);\n }\n };\n\n const handleTerminateSession = async (session: Session) => {\n try {\n await terminateSession({ variables: { id: session.id } });\n } catch (err) {\n console.error('Failed to terminate session:', err);\n }\n };\n\n const getTerminalUrl = useCallback((session: Session): string | null => {\n return session.ttydUrl || null;\n }, []);\n\n // Share session handler\n const handleShareSession = useCallback((session: Session) => {\n setShareSession(session);\n }, []);\n\n const handleOpenTerminal = useCallback(\n async (session: Session) => {\n const existingTab = terminalTabs.find((t) => t.sessionId === session.id);\n if (existingTab?.terminalUrl) {\n setActiveTerminalTab(session.id);\n setTerminalCollapsed(false);\n return;\n }\n\n const url = getTerminalUrl(session);\n\n if (url) {\n const newTab: TerminalTab = {\n sessionId: session.id,\n name: session.name,\n terminalUrl: url,\n loading: false,\n error: null,\n };\n setTerminalTabs((prev) => {\n const filtered = prev.filter((t) => t.sessionId !== session.id);\n return [...filtered, newTab];\n });\n setActiveTerminalTab(session.id);\n setTerminalCollapsed(false);\n return;\n }\n\n const loadingTab: TerminalTab = {\n sessionId: session.id,\n name: session.name,\n terminalUrl: null,\n loading: true,\n error: null,\n };\n setTerminalTabs((prev) => {\n const filtered = prev.filter((t) => t.sessionId !== session.id);\n return [...filtered, loadingTab];\n });\n setActiveTerminalTab(session.id);\n setTerminalCollapsed(false);\n\n try {\n const result = await startSessionTerminal({ variables: { id: session.id } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n const remainingTabs = terminalTabs.filter((t) => t.sessionId !== session.id);\n setTerminalTabs(remainingTabs);\n if (activeTerminalTab === session.id) {\n setActiveTerminalTab(\n remainingTabs.length > 0 ? remainingTabs[remainingTabs.length - 1].sessionId : null\n );\n }\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n // Apply the same browser-host normalization we use for list rows\n // so a `host.docker.internal`/`localhost` URL falls back to the\n // agent's known tunnel host where possible. The mutation response\n // doesn't fetch the agent inline (SessionFields fragment is flat),\n // so we look up the tunnelUrl from the already-loaded session row.\n const rawUrl = result.data?.startSessionTerminal?.ttydUrl || null;\n const sessionRow = sessions.find((s) => s.id === session.id);\n const tunnelHint = sessionRow?.agent?.tunnelUrl ?? null;\n const newUrl = normalizeSessionTerminalUrl(rawUrl, tunnelHint);\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === session.id\n ? {\n ...t,\n terminalUrl: newUrl,\n loading: false,\n error: newUrl\n ? null\n : tr(\n 'sessions.terminalStartedNoTunnelUrl',\n 'Terminal started but no tunnel URL was returned. Try restarting.'\n ),\n }\n : t\n )\n );\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n const remainingTabs = terminalTabs.filter((t) => t.sessionId !== session.id);\n setTerminalTabs(remainingTabs);\n if (activeTerminalTab === session.id) {\n setActiveTerminalTab(\n remainingTabs.length > 0 ? remainingTabs[remainingTabs.length - 1].sessionId : null\n );\n }\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n const message =\n err instanceof Error\n ? err.message\n : tr('sessions.failedStartTerminal', 'Failed to start terminal');\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === session.id ? { ...t, loading: false, error: message } : t\n )\n );\n }\n },\n [activeTerminalTab, terminalTabs, getTerminalUrl, startSessionTerminal]\n );\n\n useEffect(() => {\n if (!uiStateLoaded || !activeTerminalTab) return;\n if (terminalTabs.some((tab) => tab.sessionId === activeTerminalTab)) return;\n\n const session = sessions.find((item) => item.id === activeTerminalTab);\n if (session) {\n void handleOpenTerminal(session);\n }\n }, [activeTerminalTab, handleOpenTerminal, sessions, terminalTabs, uiStateLoaded]);\n\n const handleCloseTerminalTab = useCallback(\n (sessionId: string) => {\n setTerminalTabs((prev) => prev.filter((t) => t.sessionId !== sessionId));\n if (activeTerminalTab === sessionId) {\n setActiveTerminalTab(() => {\n const remaining = terminalTabs.filter((t) => t.sessionId !== sessionId);\n return remaining.length > 0 ? remaining[remaining.length - 1].sessionId : null;\n });\n }\n },\n [activeTerminalTab, terminalTabs]\n );\n\n const handleOpenTerminalExternal = useCallback(\n (sessionId: string) => {\n const tab = terminalTabs.find((t) => t.sessionId === sessionId);\n if (tab?.terminalUrl) window.open(tab.terminalUrl, '_blank');\n },\n [terminalTabs]\n );\n\n const handleRenameSession = useCallback(\n async (sessionId: string, newName: string) => {\n try {\n await updateSession({ variables: { id: sessionId, input: { name: newName } } });\n setTerminalTabs((prev) =>\n prev.map((t) => (t.sessionId === sessionId ? { ...t, name: newName } : t))\n );\n } catch (err) {\n console.error('Failed to rename session:', err);\n }\n },\n [updateSession]\n );\n\n const handleStopTerminal = useCallback(\n async (sessionId: string) => {\n try {\n await stopSessionTerminal({ variables: { id: sessionId } });\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId\n ? {\n ...t,\n terminalUrl: null,\n error: tr('sessions.terminalStopped', 'Terminal stopped'),\n }\n : t\n )\n );\n } catch (err) {\n console.error('Failed to stop terminal:', err);\n }\n },\n [stopSessionTerminal]\n );\n\n const handleRestartTerminal = useCallback(\n async (sessionId: string) => {\n const previousTab = terminalTabs.find((t) => t.sessionId === sessionId) || null;\n\n try {\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId ? { ...t, loading: true, error: null, terminalUrl: null } : t\n )\n );\n const result = await restartSessionTerminal({ variables: { id: sessionId } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId\n ? {\n ...t,\n loading: false,\n error: null,\n terminalUrl: previousTab?.terminalUrl ?? t.terminalUrl,\n }\n : t\n )\n );\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n const newUrl = result.data?.restartSessionTerminal?.ttydUrl || null;\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId\n ? {\n ...t,\n terminalUrl: newUrl,\n loading: false,\n error: newUrl\n ? null\n : tr(\n 'sessions.terminalRestartedNoUrl',\n 'Terminal restarted but no URL returned.'\n ),\n }\n : t\n )\n );\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId\n ? {\n ...t,\n loading: false,\n error: null,\n terminalUrl: previousTab?.terminalUrl ?? t.terminalUrl,\n }\n : t\n )\n );\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n const message =\n err instanceof Error\n ? err.message\n : tr('sessions.failedRestartTerminal', 'Failed to restart terminal');\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId ? { ...t, loading: false, error: message } : t\n )\n );\n }\n },\n [restartSessionTerminal, terminalTabs]\n );\n\n // Toggle immersive fullscreen mode\n const handleToggleImmersiveFullscreen = useCallback(() => {\n setIsImmersiveFullscreen((prev) => !prev);\n }, []);\n\n // Stats\n const stats = {\n total: sessions.length,\n running: sessions.filter((s) => s.status === 'RUNNING').length,\n stopped: sessions.filter((s) => s.status === 'STOPPED').length,\n };\n\n // Guard states rendered inside MotionPage to prevent unmount/remount animation replay\n if (permissionsLoading) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('sessions.loadingPermissions', 'Loading permissions...')} />\n </MotionPage>\n );\n }\n\n if (\n permissions &&\n permissions.permissionStrings?.length > 0 &&\n !canListSessions\n ) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'sessions.accessDeniedDescription',\n \"You don't have permission to view sessions.\"\n )}\n details={tr('sessions.requiredPermission', 'Required permission: session:list')}\n />\n </MotionPage>\n );\n }\n\n const isInitialLoad = loading && !previousData && sessions.length === 0;\n if (isInitialLoad) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('sessions.loading', 'Loading sessions...')} />\n </MotionPage>\n );\n }\n\n if (error && sessions.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('sessions.failedLoad', 'Failed to load sessions')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n // ── Bulk Action Bar ──────────────────────────────────────────────────\n const renderBulkActionBar = () => {\n if (!hasSelection || viewMode === 'terminal') return null;\n return (\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-3 px-4 py-2 bg-action-primary-bg/10 border border-action-primary-bg/20 rounded-lg\">\n <span className=\"text-sm font-medium text-text-primary\">\n {tr(\n selectedSessionIds.size === 1\n ? 'sessions.selectionCountSingle'\n : 'sessions.selectionCountPlural',\n selectedSessionIds.size === 1\n ? '{{count}} session selected'\n : '{{count}} sessions selected',\n { count: selectedSessionIds.size }\n )}\n </span>\n <div className=\"flex items-center gap-1.5 sm:ml-auto flex-wrap\">\n <button\n type=\"button\"\n onClick={handleBulkStop}\n disabled={bulkLoading}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <Square className=\"size-3\" /> {tr('sessions.stop', 'Stop')}\n </button>\n <button\n type=\"button\"\n onClick={handleBulkRestart}\n disabled={bulkLoading}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <RotateCcw className=\"size-3\" /> {tr('sessions.restart', 'Restart')}\n </button>\n <button\n type=\"button\"\n onClick={handleBulkTerminate}\n disabled={bulkLoading}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-status-error-text bg-status-error-bg border border-status-error-text/20 rounded-md hover:bg-status-error-bg/80 transition-colors disabled:opacity-50\"\n >\n <Trash2 className=\"size-3\" /> {tr('sessions.remove', 'Remove')}\n </button>\n <button\n type=\"button\"\n onClick={deselectAllSessions}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n <XSquare className=\"size-3\" /> {tr('sessions.deselect', 'Deselect')}\n </button>\n </div>\n </div>\n );\n };\n\n // Terminal-first view\n if (viewMode === 'terminal') {\n return (\n <MotionPage className=\"flex flex-col bg-bg-surface h-screen\">\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 px-4 py-2 border-b border-border-default bg-bg-sunken\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-sm font-semibold text-text-primary\">\n {tr('nav.sessions', 'Sessions')}\n </h1>\n <span className=\"text-xs text-text-muted\">\n {tr('sessions.runningSummary', '{{running}} running / {{total}} total', {\n running: stats.running,\n total: stats.total,\n })}\n </span>\n </div>\n <div className=\"flex w-full flex-wrap items-center gap-2 sm:w-auto sm:justify-end\">\n <button\n type=\"button\"\n onClick={() => checkSessionHealth().then(() => refetch())}\n className=\"p-1.5 text-text-secondary hover:bg-bg-surface rounded transition-colors\"\n title={tr('sessions.checkHealth', 'Check session health')}\n >\n <Activity className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => {\n refetch();\n void refetchProbes();\n }}\n disabled={loading}\n className=\"p-1.5 text-text-secondary hover:bg-bg-surface rounded transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`size-3.5 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <div className=\"flex items-center gap-0.5 rounded bg-bg-surface p-0.5\">\n <button\n type=\"button\"\n onClick={() => setViewMode('terminal')}\n className=\"p-1.5 rounded bg-bg-sunken shadow-sm\"\n title={tr('sessions.terminalViewTitle', 'Terminal view')}\n >\n <Monitor className=\"size-3.5 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className=\"p-1.5 rounded hover:bg-bg-sunken/50\"\n title={tr('sessions.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"size-3.5 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className=\"p-1.5 rounded hover:bg-bg-sunken/50\"\n title={tr('sessions.listViewTitle', 'List view')}\n >\n <List className=\"size-3.5 text-text-secondary\" />\n </button>\n </div>\n </div>\n </div>\n\n {/* Agent Notifications Banner */}\n <div className=\"px-4 pt-3\">\n <SessionNotifications showConnectionStatus={true} maxVisible={3} />\n </div>\n\n <TerminalPanel\n tabs={terminalTabs}\n activeTabId={activeTerminalTab}\n onTabSelect={setActiveTerminalTab}\n onTabClose={handleCloseTerminalTab}\n onOpenExternal={handleOpenTerminalExternal}\n collapsed={false}\n sessions={filteredSessions}\n onOpenTerminal={handleOpenTerminal}\n onRetryTerminal={handleOpenTerminal}\n onRenameSession={handleRenameSession}\n onStopTerminal={handleStopTerminal}\n onRestartTerminal={handleRestartTerminal}\n onCreateSession={handleCreateSession}\n fullScreen={true}\n isImmersiveFullscreen={isImmersiveFullscreen}\n onToggleImmersiveFullscreen={handleToggleImmersiveFullscreen}\n contextSwitcher={undefined}\n />\n\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n {showCreateForm && renderCreateModal()}\n {renderReconnectModal()}\n </MotionPage>\n );\n }\n\n // Grid/List view\n return (\n <MotionPage className=\"flex flex-col bg-bg-surface min-h-screen\">\n <div className=\"flex-1 overflow-y-auto space-y-6 p-4 sm:p-6\">\n <PageHeader\n title={tr('nav.sessions', 'Sessions')}\n description={tr('sessions.pageDescription', '{{running}} of {{total}} sessions running', {\n running: stats.running,\n total: stats.total,\n })}\n actions={\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => checkSessionHealth().then(() => refetch())}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors\"\n title={tr('sessions.checkHealth', 'Check session health')}\n >\n <Activity className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('sessions.refreshTitle', 'Refresh sessions')}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <SaveToFilesButton\n label={tr('sessions.export', 'Export')}\n disabled={isExporting}\n testId=\"sessions-export\"\n defaultFolderPath=\"/Vibecontrols/Sessions\"\n source={{\n kind: 'blob',\n filename: `sessions-export-${new Date().toISOString().slice(0, 10)}.csv`,\n mimeType: 'text/csv',\n getBlob: async () => {\n const result = await exportSessions({\n variables: {\n input: {\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'RUNNING' | 'STOPPED' | 'STARTING' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as 'TMUX' | 'TERMINAL' | 'SSH' | 'SCRIPT' | 'CUSTOM')\n : undefined,\n vibeId:\n vibeFilter !== 'all' && vibeFilter !== 'none'\n ? vibeFilter\n : undefined,\n agentId: agentFilter !== 'all' ? agentFilter : undefined,\n },\n format: 'CSV',\n },\n },\n });\n const exportData = result.data?.exportSessions;\n if (!exportData) return null;\n return { blob: new Blob([exportData.data], { type: 'text/csv' }) };\n },\n }}\n onDownload={() => handleExportSessions('CSV')}\n />\n <PermissionButton permission={VIBE_PERMISSIONS.SESSION_CREATE}>\n <button\n type=\"button\"\n onClick={() => setShowReconnectModal(true)}\n className=\"flex items-center gap-2 px-3 py-2 text-text-primary border border-border-default rounded-md hover:bg-bg-sunken transition-colors\"\n data-testid=\"sessions-reconnect-button\"\n title={tr(\n 'sessions.reconnectTooltip',\n 'Reconnect orphan tmux/wezterm/zellij sessions left over from a previous agent run'\n )}\n >\n <RefreshCw className=\"size-4\" />\n {tr('sessions.reconnect', 'Reconnect')}\n </button>\n </PermissionButton>\n <PermissionButton permission={VIBE_PERMISSIONS.SESSION_CREATE}>\n <button\n type=\"button\"\n onClick={handleCreateSession}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('sessions.newSession', 'New Session')}\n </button>\n </PermissionButton>\n </div>\n }\n />\n\n {/* Agent Notifications Banner */}\n <SessionNotifications showConnectionStatus={true} maxVisible={3} />\n\n {/* Filters Bar */}\n <div data-tour=\"sessions-filters\" className=\"space-y-3\">\n {/* Row 1: Search + View Toggle */}\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('sessions.searchPlaceholder', 'Search sessions...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setViewMode('terminal')}\n className=\"p-1.5 sm:p-2 rounded hover:bg-bg-surface/50\"\n title={tr('sessions.terminalViewTitle', 'Terminal view')}\n >\n <Monitor className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${(viewMode as ViewMode) === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('sessions.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${(viewMode as ViewMode) === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('sessions.listViewTitle', 'List view')}\n >\n <List className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Filters - wrap on all screens */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('sessions.filterStatus', 'Status')}\n value={statusFilter}\n options={statusOptions}\n onChange={setStatusFilter}\n />\n <FilterDropdown\n label={tr('sessions.filterType', 'Type')}\n value={typeFilter}\n options={typeOptions}\n onChange={setTypeFilter}\n />\n <FilterDropdown\n label={tr('sessions.filterVibe', 'Vibe')}\n value={vibeFilter}\n options={vibeOptions}\n onChange={setVibeFilter}\n />\n <FilterDropdown\n label={tr('sessions.filterAgent', 'Agent')}\n value={agentFilter}\n options={agentOptions}\n onChange={setAgentFilter}\n />\n </div>\n </div>\n\n {/* Quick Actions & Select All */}\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-2 flex-wrap\">\n <button\n type=\"button\"\n onClick={() => setStatusFilter('RUNNING')}\n className={`flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${\n statusFilter === 'RUNNING'\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n <Play className=\"size-3\" /> {tr('sessions.running', 'Running')} ({stats.running})\n </button>\n <button\n type=\"button\"\n onClick={() => setStatusFilter('STOPPED')}\n className={`flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${\n statusFilter === 'STOPPED'\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n <Square className=\"size-3\" /> {tr('sessions.stoppedState', 'Stopped')} ({stats.stopped})\n </button>\n <div className=\"sm:ml-auto flex items-center gap-2\">\n {filteredSessions.length > 0 && (\n <button\n type=\"button\"\n onClick={isAllSelected ? deselectAllSessions : selectAllSessions}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n <CheckSquare className=\"size-3.5\" />\n {isAllSelected\n ? tr('sessions.deselectAll', 'Deselect All')\n : tr('sessions.selectAll', 'Select All')}\n </button>\n )}\n </div>\n </div>\n\n {/* Bulk Action Bar */}\n {renderBulkActionBar()}\n\n {/* Results Count */}\n {searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n vibeFilter !== 'all' ||\n agentFilter !== 'all' ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('sessions.showing', 'Showing')} {filteredSessions.length} {tr('sessions.of', 'of')}{' '}\n {sessions.length} {tr('nav.sessions', 'sessions')}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n ({tr('sessions.refreshing', 'refreshing...')})\n </span>\n )}\n </p>\n ) : null}\n\n {/* Sessions Display */}\n <AnimatePresence mode=\"wait\">\n {filteredSessions.length > 0 ? (\n viewMode === 'grid' ? (\n <MotionList\n key=\"grid-view\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredSessions.map((session) => (\n <MotionItem key={session.id}>\n <SessionCard\n session={session}\n onOpen={handleSessionSelect}\n onStart={handleStartSession}\n onStop={handleStopSession}\n onRestart={handleRestartSession}\n onOpenTerminal={handleOpenTerminal}\n onShare={handleShareSession}\n selected={selectedSessionIds.has(session.id)}\n onToggleSelect={() => toggleSessionSelection(session.id)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <MotionItem key=\"list-view\">\n <SessionList\n sessions={filteredSessions}\n onOpen={handleSessionSelect}\n onStart={handleStartSession}\n onStop={handleStopSession}\n onRestart={handleRestartSession}\n onTerminate={handleTerminateSession}\n onOpenTerminal={handleOpenTerminal}\n onShare={handleShareSession}\n selectedIds={selectedSessionIds}\n onToggleSelect={toggleSessionSelection}\n onSelectAll={selectAllSessions}\n onDeselectAll={deselectAllSessions}\n />\n </MotionItem>\n )\n ) : (\n <div className=\"space-y-4\">\n <AgentSetupBanner variant=\"compact\" dismissKey=\"vibecontrols-sessions-setup-banner\" />\n <EmptyState\n icon={<Terminal className=\"size-8 text-text-secondary\" />}\n title={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n vibeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('sessions.emptyFound', 'No sessions found')\n : tr('sessions.emptyConfigured', 'No sessions configured')\n }\n description={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n vibeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('sessions.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'sessions.emptyDescription',\n 'Sessions require an active agent. Install the CLI agent first, then start a session.'\n )\n }\n action={\n !searchQuery &&\n statusFilter === 'all' &&\n typeFilter === 'all' &&\n vibeFilter === 'all' &&\n agentFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n {canCreateSession && (\n <button\n type=\"button\"\n onClick={handleCreateSession}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" /> {tr('sessions.newSession', 'New Session')}\n </button>\n )}\n <GetHelpButton prompt=\"What is a Session in VibeControls and how do I start one?\" />\n </div>\n ) : undefined\n }\n />\n </div>\n )}\n </AnimatePresence>\n\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n </div>\n\n {/* Tabbed Terminal Panel - docked at bottom */}\n {terminalTabs.length > 0 && (\n <TerminalPanel\n tabs={terminalTabs}\n activeTabId={activeTerminalTab}\n onTabSelect={setActiveTerminalTab}\n onTabClose={handleCloseTerminalTab}\n onOpenExternal={handleOpenTerminalExternal}\n onToggleCollapse={() => setTerminalCollapsed((prev) => !prev)}\n collapsed={terminalCollapsed}\n sessions={filteredSessions}\n onOpenTerminal={handleOpenTerminal}\n onRetryTerminal={handleOpenTerminal}\n onRenameSession={handleRenameSession}\n onStopTerminal={handleStopTerminal}\n onRestartTerminal={handleRestartTerminal}\n heightClass=\"h-[280px] sm:h-[380px]\"\n isImmersiveFullscreen={isImmersiveFullscreen}\n onToggleImmersiveFullscreen={handleToggleImmersiveFullscreen}\n contextSwitcher={undefined}\n />\n )}\n\n {showCreateForm && renderCreateModal()}\n {renderReconnectModal()}\n\n {/* Share Session Dialog */}\n {shareSession && (\n <SessionShareDialog\n session={shareSession}\n isOpen={!!shareSession}\n onClose={() => setShareSession(null)}\n />\n )}\n\n <ConfirmDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title={getQuotaDialogContent('session').title}\n message={getQuotaDialogContent('session').message}\n confirmLabel=\"View Billing\"\n cancelLabel=\"Dismiss\"\n variant=\"warning\"\n />\n </MotionPage>\n );\n\n function renderReconnectModal() {\n if (!showReconnectModal) return null;\n const orphans = orphansData?.agentSessionOrphans ?? [];\n return (\n <div\n className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\"\n data-testid=\"sessions-reconnect-modal\"\n >\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-2xl max-h-[80vh] overflow-y-auto\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-1\">\n {tr('sessions.reconnectTitle', 'Reconnect orphan sessions')}\n </h2>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tr(\n 'sessions.reconnectDesc',\n 'Pick an agent to scan its host for tmux / wezterm / zellij sessions named vibe-* that are not currently tracked. Adopt one to start a web terminal and bring it back into the UI.'\n )}\n </p>\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.agent', 'Agent')}\n </label>\n <SearchableSelect\n options={createAgentOptions}\n value={reconnectAgentId}\n onChange={(id) => {\n setReconnectAgentId(id);\n setReconnectError(null);\n if (id) void discoverOrphans({ variables: { agentId: id } });\n }}\n placeholder={tr('sessions.selectAgent', 'Select an agent')}\n searchPlaceholder={tr('sessions.searchAgents', 'Search agents...')}\n clearable\n />\n </div>\n {reconnectError && (\n <div\n className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\"\n data-testid=\"sessions-reconnect-error\"\n >\n {reconnectError}\n </div>\n )}\n {reconnectAgentId && (\n <div data-testid=\"sessions-reconnect-list\" className=\"space-y-2\">\n {orphansLoading ? (\n <div className=\"text-sm text-text-secondary\">\n {tr('sessions.scanningHost', 'Scanning host...')}\n </div>\n ) : orphans.length === 0 ? (\n <div className=\"text-sm text-text-secondary\">\n {tr('sessions.noOrphans', 'No orphan vibe-* sessions found on this agent.')}\n </div>\n ) : (\n orphans.map(\n (o: {\n externalName: string;\n provider: string;\n windows: number;\n attached: boolean;\n createdAt?: string | null;\n }) => (\n <div\n key={o.externalName}\n className=\"flex items-center justify-between gap-3 p-3 border border-border-default rounded-md bg-bg-sunken/40\"\n data-testid=\"sessions-reconnect-row\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"font-mono text-sm text-text-primary break-all\">\n {o.externalName}\n </div>\n <div className=\"text-xs text-text-tertiary mt-0.5\">\n {o.provider} · {o.windows} window{o.windows !== 1 ? 's' : ''}\n {o.attached ? ' · attached' : ''}\n {o.createdAt ? ` · created ${o.createdAt}` : ''}\n </div>\n </div>\n <button\n type=\"button\"\n disabled={reconnectAdoptingName === o.externalName}\n onClick={async () => {\n setReconnectAdoptingName(o.externalName);\n setReconnectError(null);\n try {\n await adoptAgentSession({\n variables: {\n agentId: reconnectAgentId,\n externalName: o.externalName,\n name: o.externalName,\n },\n });\n // Refresh orphan list so the adopted one disappears.\n await discoverOrphans({\n variables: { agentId: reconnectAgentId },\n });\n } catch (e) {\n setReconnectError(e instanceof Error ? e.message : String(e));\n } finally {\n setReconnectAdoptingName(null);\n }\n }}\n className=\"shrink-0 px-3 py-1.5 text-xs bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n data-testid={`sessions-reconnect-adopt-${o.externalName}`}\n >\n {reconnectAdoptingName === o.externalName\n ? tr('sessions.adopting', 'Adopting…')\n : tr('sessions.adopt', 'Adopt')}\n </button>\n </div>\n )\n )\n )}\n </div>\n )}\n </div>\n <div className=\"flex justify-end gap-3 mt-6\">\n <button\n type=\"button\"\n onClick={() => {\n setShowReconnectModal(false);\n setReconnectError(null);\n }}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n {tr('common.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n function renderCreateModal() {\n return (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('sessions.newSession', 'New Session')}\n </h2>\n <div className=\"space-y-4\">\n {createError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {createError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.name', 'Name')}\n </label>\n <input\n aria-label=\"control\"\n ref={sessionNameRef}\n type=\"text\"\n placeholder={tr('sessions.namePlaceholder', 'dev-server')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.filterVibe', 'Vibe')}\n </label>\n <SearchableSelect\n options={createVibeOptions}\n value={formVibeId}\n onChange={setFormVibeId}\n placeholder={tr('sessions.noVibe', 'No vibe')}\n searchPlaceholder={tr('sessions.searchVibes', 'Search vibes...')}\n clearable\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.filterAgent', 'Agent')}\n </label>\n <SearchableSelect\n options={createAgentOptions}\n value={formAgentId}\n onChange={setFormAgentId}\n placeholder={tr('sessions.selectAgent', 'Select an agent')}\n searchPlaceholder={tr('sessions.searchAgents', 'Search agents...')}\n clearable\n />\n <p className=\"text-xs text-text-muted mt-1\">\n {tr(\n 'sessions.agentHint',\n \"Required when no vibe is selected. Overridden by the vibe's agent if a vibe is chosen.\"\n )}\n </p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.filterType', 'Type')}\n </label>\n <SearchableSelect\n options={sessionTypeOptions}\n value={formType}\n onChange={setFormType}\n placeholder={tr('sessions.selectType', 'Select type')}\n searchPlaceholder={tr('sessions.searchTypes', 'Search types...')}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.command', 'Command')}\n </label>\n <input\n aria-label=\"control\"\n ref={sessionCommandRef}\n type=\"text\"\n placeholder={tr('sessions.commandPlaceholder', 'bun run dev')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <input\n aria-label=\"autoStart\"\n ref={sessionAutoStartRef}\n type=\"checkbox\"\n id=\"autoStart\"\n className=\"size-4 rounded border-border-default\"\n />\n <label htmlFor=\"autoStart\" className=\"text-sm text-text-primary\">\n {tr('sessions.autoStartWithVibe', 'Auto-start with vibe')}\n </label>\n </div>\n {(formType === 'TMUX' || formType === 'WEZTERM' || formType === 'ZELLIJ') && (\n <div\n className=\"rounded-md border border-border-default bg-bg-sunken/40\"\n data-testid=\"sessions-create-advanced\"\n >\n <button\n type=\"button\"\n onClick={() => setFormShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between px-3 py-2 text-sm font-medium text-text-primary\"\n aria-expanded={formShowAdvanced}\n >\n <span>{tr('sessions.advanced', 'Advanced options')}</span>\n <span className=\"text-text-tertiary text-xs\">{formShowAdvanced ? '▾' : '▸'}</span>\n </button>\n {formShowAdvanced && (\n <div className=\"px-3 pb-3 space-y-2\">\n <label className=\"block text-xs font-medium text-text-secondary\">\n {tr('sessions.attachExistingLabel', 'Attach to existing session (optional)')}\n </label>\n <input\n aria-label=\"control\"\n ref={sessionExternalNameRef}\n type=\"text\"\n data-testid=\"sessions-create-external-name\"\n placeholder={\n formType === 'WEZTERM'\n ? 'e.g. vibe-051c9c62 (existing wezterm workspace)'\n : formType === 'ZELLIJ'\n ? 'e.g. vibe-051c9c62 (existing zellij session)'\n : 'e.g. vibe-051c9c62 (existing tmux session)'\n }\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder font-mono\"\n />\n <p className=\"text-xs text-text-tertiary\">\n {tr(\n 'sessions.attachExistingHint',\n 'When set, the agent attaches to that existing session instead of creating a new one. Leave blank to spawn fresh.'\n )}\n </p>\n </div>\n )}\n </div>\n )}\n </div>\n <div className=\"border-t border-border-default pt-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('sessions.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_SESSION\" onDraftChange={setDraftTagIds} />\n </div>\n <div className=\"flex justify-end gap-3 mt-6\">\n <button\n type=\"button\"\n onClick={() => setShowCreateForm(false)}\n disabled={createLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSubmitCreateSession}\n disabled={createLoading}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {createLoading\n ? tr('sessions.creating', 'Creating...')\n : tr('sessions.createSession', 'Create Session')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SessionsPage.js","names":[],"sources":["../../src/pages/SessionsPage.tsx"],"sourcesContent":["import { useState, useMemo, useRef, useCallback, useEffect } from 'react';\nimport {\n Plus,\n Terminal,\n Grid,\n List,\n Play,\n Square,\n RefreshCw,\n Monitor,\n Trash2,\n RotateCcw,\n CheckSquare,\n XSquare,\n Activity,\n Download,\n} from 'lucide-react';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { SessionCard, SessionList, TerminalPanel, SessionShareDialog } from '@/components/sessions';\nimport type { Session, TerminalTab } from '@/components/sessions';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n AgentSetupBanner,\n SearchableSelect,\n SessionNotifications,\n GetHelpButton,\n ConfirmDialog,\n} from '@/components/shared';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport {\n useListSessionsQuery,\n useVibecontrolsAgentHealthProbesLazyQuery,\n useVibecontrolsAgentHealthProbeStreamSubscription,\n useStartSessionMutation,\n useStopSessionMutation,\n useRestartSessionMutation,\n useCreateSessionMutation,\n useStartSessionTerminalMutation,\n useStopSessionTerminalMutation,\n useRestartSessionTerminalMutation,\n useUpdateSessionMutation,\n useTerminateSessionMutation,\n useCheckSessionHealthMutation,\n useSessionStatusChangedSubscription,\n useBulkStopSessionsMutation,\n useBulkRestartSessionsMutation,\n useBulkTerminateSessionsMutation,\n useAgentSessionOrphansLazyQuery,\n useAdoptAgentSessionMutation,\n useExportSessionsMutation,\n} from '@/generated/wspace-operations';\nimport { PermissionButton, AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { SaveToFilesButton } from '@burdenoff/fe-libs/storage';\nimport { useSearchParams } from 'react-router-dom';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n getQuotaDialogContent,\n markQuotaHandledLocally,\n isQuotaExhaustedError,\n} from '@/utils/quotaUtils';\n\ntype ViewMode = 'terminal' | 'grid' | 'list';\n\n// Long-tail health-check fallback. The primary path is the\n// SessionStatusChanged subscription (sub-second updates). This poll only\n// fires if the subscription is dropped; 5m is fine because the server\n// also runs a throttled background health check on every ListSessions\n// query.\nconst HEALTH_CHECK_INTERVAL_MS = 300_000;\n\nfunction normalizeSessionTerminalUrl(\n terminalUrl: string | null | undefined,\n tunnelUrl: string | null | undefined\n): string | null {\n if (!terminalUrl) return null;\n\n try {\n const parsedTerminalUrl = new URL(terminalUrl);\n const blockedHosts = new Set(['localhost', '127.0.0.1', 'host.docker.internal']);\n\n if (tunnelUrl && blockedHosts.has(parsedTerminalUrl.hostname)) {\n const parsedTunnelUrl = new URL(tunnelUrl);\n parsedTunnelUrl.pathname = parsedTerminalUrl.pathname;\n parsedTunnelUrl.search = parsedTerminalUrl.search;\n parsedTunnelUrl.hash = parsedTerminalUrl.hash;\n return parsedTunnelUrl.toString();\n }\n\n return parsedTerminalUrl.toString();\n } catch {\n return terminalUrl;\n }\n}\n\nfunction deriveBrowserSessionStatus(\n status: Session['status'],\n rawTerminalUrl: string | null | undefined,\n normalizedTerminalUrl: string | null,\n agentIsActive?: boolean\n): Session['status'] {\n if (status !== 'RUNNING') return status;\n if (agentIsActive === false) return 'ERROR';\n if (!rawTerminalUrl) return status;\n if (!normalizedTerminalUrl) return 'ERROR';\n\n try {\n const pageProtocol = typeof window !== 'undefined' ? window.location.protocol : 'https:';\n const terminalProtocol = new URL(normalizedTerminalUrl).protocol;\n if (pageProtocol === 'https:' && terminalProtocol !== 'https:') {\n return 'ERROR';\n }\n } catch {\n return 'ERROR';\n }\n\n return status;\n}\n\n/**\n * Sessions management page with terminal-first UI.\n * Persists tab state, supports bulk operations, keyboard shortcuts, and health polling.\n */\nexport function SessionsPage() {\n const { t } = useI18n();\n const { onSessionStart, workspaceId } = useVibeControls();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const [searchParams, setSearchParams] = useSearchParams();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const statusOptions = useMemo(\n () => [\n { value: 'all', label: tr('sessions.statusAll', 'All Statuses') },\n { value: 'RUNNING', label: tr('sessions.running', 'Running') },\n { value: 'STOPPED', label: tr('sessions.stopped', 'Stopped') },\n { value: 'STARTING', label: tr('sessions.starting', 'Starting') },\n { value: 'ERROR', label: tr('common.error', 'Error') },\n ],\n [tr]\n );\n\n const typeOptions = useMemo(\n () => [\n { value: 'all', label: tr('sessions.typeAll', 'All Types') },\n { value: 'TMUX', label: tr('sessions.typeTmux', 'Tmux') },\n { value: 'TERMINAL', label: tr('sessions.typeTerminal', 'Terminal') },\n { value: 'SCRIPT', label: tr('sessions.typeScript', 'Script') },\n { value: 'CUSTOM', label: tr('sessions.typeCustom', 'Custom') },\n ],\n [tr]\n );\n\n // Check permissions\n const canListSessions = hasPermission(VIBE_PERMISSIONS.SESSION_LIST);\n const canCreateSession = hasPermission(VIBE_PERMISSIONS.SESSION_CREATE);\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState('all');\n const [typeFilter, setTypeFilter] = useState('all');\n const [vibeFilter, setVibeFilter] = useState('all');\n const [agentFilter, setAgentFilter] = useState('all');\n const [viewMode, setViewMode] = useState<ViewMode>('terminal');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [createLoading, setCreateLoading] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n // Bulk selection state\n const [selectedSessionIds, setSelectedSessionIds] = useState<Set<string>>(new Set());\n const [bulkLoading, setBulkLoading] = useState(false);\n const [isExporting, setIsExporting] = useState(false);\n\n // Form refs\n const sessionNameRef = useRef<HTMLInputElement>(null);\n const sessionCommandRef = useRef<HTMLInputElement>(null);\n const sessionAutoStartRef = useRef<HTMLInputElement>(null);\n // Advanced: provider-native session name to attach to (tmux/wezterm/zellij).\n // When set on submit, the agent reattaches to an existing daemon-side\n // session instead of spawning a fresh one. Empty = normal create.\n const sessionExternalNameRef = useRef<HTMLInputElement>(null);\n\n // Controlled form state for searchable selects\n const [formVibeId, setFormVibeId] = useState('');\n const [formAgentId, setFormAgentId] = useState('');\n const [formType, setFormType] = useState('TMUX');\n const [formShowAdvanced, setFormShowAdvanced] = useState(false);\n\n // Terminal panel state - use store for persistence across navigation\n const {\n terminalTabs: persistedTabs,\n activeTerminalTabId: persistedActiveTab,\n sessionsViewMode: persistedViewMode,\n setTerminalTabs: setPersistedTabs,\n setActiveTerminalTabId: setPersistedActiveTab,\n setSessionsViewMode: setPersistedViewMode,\n activeSessionId,\n } = useVibeControlsStore();\n\n // Local terminal tabs state (enriched with loading/error/url)\n const [terminalTabs, setTerminalTabs] = useState<TerminalTab[]>([]);\n const [activeTerminalTab, setActiveTerminalTab] = useState<string | null>(null);\n const [terminalCollapsed, setTerminalCollapsed] = useState(false);\n\n // Share dialog state\n const [shareSession, setShareSession] = useState<Session | null>(null);\n\n // Immersive fullscreen state (terminal takes over the entire screen)\n const [isImmersiveFullscreen, setIsImmersiveFullscreen] = useState(false);\n\n // UI state persistence flag — true once we've loaded from store\n const [uiStateLoaded, setUiStateLoaded] = useState(false);\n\n const queryViewMode = searchParams.get('view');\n const querySessionId = searchParams.get('session');\n\n // Mutations\n const [createSession] = useCreateSessionMutation({ refetchQueries: ['ListSessions'] });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_SESSION');\n\n // Reconnect (orphan adoption) modal state. Independent from the\n // New Session form so the user can reconnect without prefilling the\n // create form. Opened via the \"Reconnect\" header button.\n const [showReconnectModal, setShowReconnectModal] = useState(false);\n const [reconnectAgentId, setReconnectAgentId] = useState<string>('');\n const [reconnectError, setReconnectError] = useState<string | null>(null);\n const [reconnectAdoptingName, setReconnectAdoptingName] = useState<string | null>(null);\n const [discoverOrphans, { data: orphansData, loading: orphansLoading }] =\n useAgentSessionOrphansLazyQuery({ fetchPolicy: 'network-only' });\n const [adoptAgentSession] = useAdoptAgentSessionMutation({\n refetchQueries: ['ListSessions'],\n });\n const [startSession] = useStartSessionMutation({ refetchQueries: ['ListSessions'] });\n const [stopSession] = useStopSessionMutation({ refetchQueries: ['ListSessions'] });\n const [restartSession] = useRestartSessionMutation({ refetchQueries: ['ListSessions'] });\n const [startSessionTerminal] = useStartSessionTerminalMutation({\n refetchQueries: ['ListSessions'],\n });\n const [stopSessionTerminal] = useStopSessionTerminalMutation({\n refetchQueries: ['ListSessions'],\n });\n const [restartSessionTerminal] = useRestartSessionTerminalMutation({\n refetchQueries: ['ListSessions'],\n });\n const [updateSession] = useUpdateSessionMutation({ refetchQueries: ['ListSessions'] });\n const [terminateSession] = useTerminateSessionMutation({ refetchQueries: ['ListSessions'] });\n const [checkSessionHealth] = useCheckSessionHealthMutation({ refetchQueries: ['ListSessions'] });\n const [bulkStopSessions] = useBulkStopSessionsMutation({ refetchQueries: ['ListSessions'] });\n const [bulkRestartSessions] = useBulkRestartSessionsMutation({\n refetchQueries: ['ListSessions'],\n });\n const [bulkTerminateSessions] = useBulkTerminateSessionsMutation({\n refetchQueries: ['ListSessions'],\n });\n const [exportSessions] = useExportSessionsMutation();\n\n // ── UI State Persistence (localStorage via Zustand store) ─────────────\n // Load persisted UI state from store on mount\n useEffect(() => {\n if (uiStateLoaded) return;\n\n // Query params take precedence over persisted UI state so session tabs are bookmarkable.\n if (queryViewMode === 'terminal' || queryViewMode === 'grid' || queryViewMode === 'list') {\n setViewMode(queryViewMode);\n } else if (persistedViewMode) {\n setViewMode(persistedViewMode);\n }\n\n if (querySessionId) {\n setActiveTerminalTab(querySessionId);\n } else if (persistedActiveTab) {\n setActiveTerminalTab(persistedActiveTab);\n }\n\n // Restore tabs from store — they'll need to re-fetch terminal URLs\n if (persistedTabs.length > 0) {\n const restoredTabs: TerminalTab[] = persistedTabs.map((t) => ({\n sessionId: t.sessionId,\n name: t.name,\n terminalUrl: null, // Will be populated when sessions load\n loading: false,\n error: null,\n }));\n setTerminalTabs(restoredTabs);\n }\n\n setUiStateLoaded(true);\n }, [\n persistedTabs,\n persistedActiveTab,\n persistedViewMode,\n querySessionId,\n queryViewMode,\n uiStateLoaded,\n ]);\n\n // Once sessions are loaded and we have restored tabs, populate terminal URLs\n const sessionsLoadedRef = useRef(false);\n\n // Sync local state changes to store for persistence\n useEffect(() => {\n if (!uiStateLoaded) return;\n // Sync terminal tabs to store (only sessionId and name for persistence)\n const tabsToStore = terminalTabs.map((t) => ({ sessionId: t.sessionId, name: t.name }));\n setPersistedTabs(tabsToStore);\n }, [terminalTabs, uiStateLoaded, setPersistedTabs]);\n\n useEffect(() => {\n if (!uiStateLoaded) return;\n setPersistedActiveTab(activeTerminalTab);\n }, [activeTerminalTab, uiStateLoaded, setPersistedActiveTab]);\n\n useEffect(() => {\n if (!uiStateLoaded) return;\n setPersistedViewMode(viewMode);\n }, [viewMode, uiStateLoaded, setPersistedViewMode]);\n\n useEffect(() => {\n if (!uiStateLoaded) return;\n\n const next = new URLSearchParams(searchParams);\n let changed = false;\n\n const nextView = viewMode === 'terminal' ? null : viewMode;\n if (nextView) {\n if (next.get('view') !== nextView) {\n next.set('view', nextView);\n changed = true;\n }\n } else if (next.has('view')) {\n next.delete('view');\n changed = true;\n }\n\n if (activeTerminalTab) {\n if (next.get('session') !== activeTerminalTab) {\n next.set('session', activeTerminalTab);\n changed = true;\n }\n } else if (next.has('session')) {\n next.delete('session');\n changed = true;\n }\n\n if (changed) {\n setSearchParams(next, { replace: true });\n }\n }, [activeTerminalTab, searchParams, setSearchParams, uiStateLoaded, viewMode]);\n\n // Track previous activeSessionId to detect changes from the context switcher\n const prevActiveSessionIdRef = useRef<string | null>(null);\n\n // ── Live session-status subscription ─────────────────────────────────\n // Replaces the prior `setInterval(checkSessionHealth, …)` poll. The\n // backend already runs a throttled background health check on every\n // ListSessions query (see SessionService.triggerBackgroundHealthCheck);\n // the subscription propagates resulting status flips into Apollo's\n // normalized cache by id, so any rendered session row updates without\n // a manual refetch.\n //\n // workspaceId comes from VibeControlsProvider; skipping until it\n // resolves avoids a \"Workspace context required\" error during initial\n // mount before the auth token is issued.\n useSessionStatusChangedSubscription({\n skip: !workspaceId,\n variables: { workspaceId: workspaceId || '' },\n });\n\n // Long-tail safety fallback: in case the subscription drops (network\n // blip, agent restart) and a status flip is missed, re-trigger the\n // server-side health check every 5 minutes. Server-side throttling\n // (30s) protects against thundering-herd if multiple tabs are open.\n useEffect(() => {\n const interval = setInterval(() => {\n checkSessionHealth().catch(() => {\n /* silent — fallback path */\n });\n }, HEALTH_CHECK_INTERVAL_MS);\n return () => clearInterval(interval);\n }, [checkSessionHealth]);\n\n // ── Keyboard Shortcuts for Terminal Tabs ────────────────────────────\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Don't intercept when typing in inputs/textareas\n const target = e.target as HTMLElement;\n if (\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.tagName === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n\n const isMod = e.metaKey || e.ctrlKey;\n\n // Ctrl/Cmd+T — New terminal tab (open session picker)\n if (isMod && e.key === 't') {\n e.preventDefault();\n setShowCreateForm(true);\n return;\n }\n\n // Ctrl/Cmd+W — Close active terminal tab\n if (isMod && e.key === 'w' && activeTerminalTab) {\n e.preventDefault();\n const sessionIdToClose = activeTerminalTab;\n setTerminalTabs((prev) => prev.filter((t) => t.sessionId !== sessionIdToClose));\n setActiveTerminalTab(() => {\n const remaining = terminalTabs.filter((t) => t.sessionId !== sessionIdToClose);\n return remaining.length > 0 ? remaining[remaining.length - 1].sessionId : null;\n });\n return;\n }\n\n // Ctrl+Tab — Next terminal tab\n if (e.ctrlKey && e.key === 'Tab' && !e.shiftKey && terminalTabs.length > 1) {\n e.preventDefault();\n const currentIdx = terminalTabs.findIndex((t) => t.sessionId === activeTerminalTab);\n const nextIdx = (currentIdx + 1) % terminalTabs.length;\n setActiveTerminalTab(terminalTabs[nextIdx].sessionId);\n return;\n }\n\n // Ctrl+Shift+Tab — Previous terminal tab\n if (e.ctrlKey && e.key === 'Tab' && e.shiftKey && terminalTabs.length > 1) {\n e.preventDefault();\n const currentIdx = terminalTabs.findIndex((t) => t.sessionId === activeTerminalTab);\n const prevIdx = (currentIdx - 1 + terminalTabs.length) % terminalTabs.length;\n setActiveTerminalTab(terminalTabs[prevIdx].sessionId);\n return;\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [activeTerminalTab, terminalTabs]);\n\n // Fetch sessions from GraphQL API (with filters)\n // Use previousData to prevent flash of loading state during refetches\n // When no filters narrow the result set, the query variables match\n // the InitialData prefetch — use cache-first to skip the redundant\n // network hit. With filters applied, cache-and-network keeps stale\n // narrowed results from being shown silently.\n const noSessionFilters =\n statusFilter === 'all' && typeFilter === 'all' && vibeFilter === 'all' && agentFilter === 'all';\n const sessionsFetchPolicy = noSessionFilters ? 'cache-first' : 'cache-and-network';\n const { data, previousData, loading, error, refetch } = useListSessionsQuery({\n variables: {\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'RUNNING' | 'STOPPED' | 'STARTING' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as 'TMUX' | 'TERMINAL' | 'SSH' | 'SCRIPT' | 'CUSTOM')\n : undefined,\n vibeId: vibeFilter !== 'all' && vibeFilter !== 'none' ? vibeFilter : undefined,\n agentId: agentFilter !== 'all' ? agentFilter : undefined,\n },\n pagination: { limit: 50 },\n },\n fetchPolicy: sessionsFetchPolicy,\n });\n\n // Use current data or fall back to previous data during refetches\n // This prevents the \"Loading sessions...\" flash when refetching\n const effectiveData = data ?? previousData;\n\n // Use cached initial data for dropdowns (agents and vibes)\n // This reads from Apollo cache - no network request\n const { agents: cachedAgents, vibes: cachedVibes } = useCachedInitialData();\n\n // Live deep-health probe per agent (every 30s). Used to gate the\n // \"Create session\" button — without this, the button stays enabled\n // against an agent whose cloudflared tunnel has died, and clicking\n // it produces the user-visible \"Failed to start terminal\" error\n // long after the session row is already created in PENDING state.\n // Initial query primes the cache; the subscription below replaces\n // the prior 30s pollInterval so health flips reach this page in <1s.\n // Probe query is lazy — the subscription below is the source of truth\n // for initial paint, so we only fire the query on user-triggered\n // refresh (see refetch handler below).\n const [refetchProbes, agentHealthQuery] = useVibecontrolsAgentHealthProbesLazyQuery({\n fetchPolicy: 'network-only',\n notifyOnNetworkStatusChange: true,\n });\n const { data: agentHealthStreamData } = useVibecontrolsAgentHealthProbeStreamSubscription();\n const agentHealthByIdRef = useMemo(() => {\n const m = new Map<string, { healthy: boolean; error: string | null }>();\n const probes =\n agentHealthStreamData?.vibecontrolsAgentHealthProbeStream ??\n agentHealthQuery.data?.vibecontrolsAgentHealthProbes ??\n [];\n for (const p of probes) {\n m.set(p.agentId, { healthy: p.healthy, error: p.errorMessage ?? null });\n }\n return m;\n }, [agentHealthQuery.data, agentHealthStreamData]);\n\n // Build vibe options for filter using cached data\n const vibeOptions = useMemo(() => {\n return [\n { value: 'all', label: tr('sessions.filterVibeAll', 'All Vibes') },\n { value: 'none', label: tr('sessions.filterVibeNone', 'No Vibe') },\n ...cachedVibes.map((vibe) => ({\n value: vibe.id,\n label: vibe.name,\n })),\n ];\n }, [cachedVibes, tr]);\n\n // Build agent options for filter using cached data\n const agentOptions = useMemo(() => {\n return [\n { value: 'all', label: tr('sessions.filterAgentAll', 'All Agents') },\n ...cachedAgents.map((agent) => ({\n value: agent.id,\n label: `${agent.name} ${agent.isActive ? '' : `(${tr('sessions.offline', 'Offline')})`}`,\n })),\n ];\n }, [cachedAgents, tr]);\n\n // Build options for create form using cached data (no \"all\"/\"none\" entries)\n const createVibeOptions = useMemo(() => {\n return [\n { value: '', label: tr('sessions.noVibe', 'No vibe') },\n ...cachedVibes.map((vibe) => ({\n value: vibe.id,\n label: vibe.name,\n })),\n ];\n }, [cachedVibes, tr]);\n\n const createAgentOptions = useMemo(() => {\n return [\n { value: '', label: tr('sessions.selectAgent', 'Select an agent') },\n ...cachedAgents.map((agent) => ({\n value: agent.id,\n label: `${agent.name} ${agent.isActive ? `(${tr('sessions.active', 'Active')})` : `(${tr('sessions.offline', 'Offline')})`}`,\n })),\n ];\n }, [cachedAgents, tr]);\n\n const sessionTypeOptions = useMemo(\n () => [\n { value: 'TMUX', label: tr('sessions.typeTmuxLower', 'tmux') },\n { value: 'WEZTERM', label: tr('sessions.typeWezTerm', 'WezTerm') },\n { value: 'ZELLIJ', label: tr('sessions.typeZellij', 'Zellij') },\n { value: 'TERMINAL', label: tr('sessions.typeTerminal', 'Terminal') },\n { value: 'SSH', label: tr('sessions.typeSsh', 'SSH') },\n { value: 'SCRIPT', label: tr('sessions.typeScript', 'Script') },\n { value: 'CUSTOM', label: tr('sessions.typeCustom', 'Custom') },\n ],\n [tr]\n );\n\n // Map GraphQL response to Session type\n // Use effectiveData to prevent flash during refetches\n const sessions: Session[] = useMemo(() => {\n const rawSessions = effectiveData?.sessions?.items ?? [];\n return rawSessions.map((session: NonNullable<typeof rawSessions>[number]) => {\n const normalizedTtydUrl = normalizeSessionTerminalUrl(\n session.ttydUrl || null,\n session.agent?.tunnelUrl || null\n );\n\n return {\n id: session.id,\n workspaceId: session.workspaceId,\n vibeId: session.vibeId || null,\n agentId: session.agentId || null,\n name: session.name,\n type: session.type as Session['type'],\n status: deriveBrowserSessionStatus(\n session.status as Session['status'],\n session.ttydUrl || null,\n normalizedTtydUrl,\n session.agent?.isActive\n ),\n command: session.command || null,\n workingDirectory: session.workingDirectory || null,\n environmentVariables: (session.environmentVariables as Record<string, string>) || {},\n autoStart: session.autoStart,\n providerSessionName: session.providerSessionName || undefined,\n providerWindowName: session.providerWindowId || undefined,\n pid: session.pid || null,\n exitCode: session.exitCode || null,\n lastOutput: session.lastOutput || null,\n ttydUrl: normalizedTtydUrl,\n metadata: (session.metadata as Record<string, unknown>) || {},\n createdAt: session.createdAt,\n updatedAt: session.updatedAt,\n vibe: session.vibe ? { id: session.vibe.id, name: session.vibe.name } : undefined,\n agent: session.agent\n ? {\n id: session.agent.id,\n name: session.agent.name,\n tunnelUrl: session.agent.tunnelUrl || null,\n isActive: session.agent.isActive,\n }\n : undefined,\n };\n });\n }, [effectiveData?.sessions?.items]);\n\n // Populate terminal URLs for restored tabs once sessions load\n useEffect(() => {\n if (sessionsLoadedRef.current || sessions.length === 0 || terminalTabs.length === 0) return;\n sessionsLoadedRef.current = true;\n\n // Re-spawn ttyd for every restored tab. The persisted ttydUrl from\n // the session row is stale on remount: the agent's ttyd process\n // dies on agent restart / idle GC, and the cached URL surfaces as\n // \"Terminal not running for this session\" when the iframe loads.\n // startSessionTerminal is idempotent server-side (re-uses an\n // existing ttyd if it's still running, spawns a fresh one if not),\n // so this is safe to call unconditionally on tab restore. The\n // iframe url returned in the mutation response is the source of\n // truth — we no longer trust the persisted ttydUrl.\n void Promise.allSettled(\n terminalTabs.map(async (tab) => {\n const session = sessions.find((s) => s.id === tab.sessionId);\n if (!session) return;\n try {\n const result = await startSessionTerminal({ variables: { id: tab.sessionId } });\n const tunnelHint = session.agent?.tunnelUrl ?? null;\n const newUrl = normalizeSessionTerminalUrl(\n result.data?.startSessionTerminal?.ttydUrl ?? null,\n tunnelHint\n );\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === tab.sessionId\n ? { ...t, terminalUrl: newUrl, loading: false, error: null }\n : t\n )\n );\n } catch (err) {\n // Don't blow up the tab — leave it loading=false with the\n // cached ttydUrl as a best-effort, the user can hit\n // \"Restart terminal\" if the iframe still 404s.\n // eslint-disable-next-line no-console\n console.warn('[sessions] tab restore startSessionTerminal failed', err);\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === tab.sessionId && session.ttydUrl\n ? { ...t, terminalUrl: session.ttydUrl, loading: false }\n : t\n )\n );\n }\n })\n );\n }, [sessions, terminalTabs, startSessionTerminal]);\n\n // ── Session Switcher Integration ─────────────────────────────────────\n // When activeSessionId changes, open/switch to that terminal\n useEffect(() => {\n // Skip if not initialized or no session selected\n if (!uiStateLoaded || !activeSessionId) {\n prevActiveSessionIdRef.current = activeSessionId;\n return;\n }\n\n // Skip if this is the initial load (not a user-initiated change)\n if (prevActiveSessionIdRef.current === null && activeSessionId) {\n prevActiveSessionIdRef.current = activeSessionId;\n return;\n }\n\n // Skip if the session hasn't actually changed\n if (prevActiveSessionIdRef.current === activeSessionId) {\n return;\n }\n\n prevActiveSessionIdRef.current = activeSessionId;\n\n // Find the session in the sessions list\n const session = sessions.find((s) => s.id === activeSessionId);\n if (!session) return;\n\n // Check if this session already has an open tab\n const existingTab = terminalTabs.find((t) => t.sessionId === activeSessionId);\n\n if (existingTab) {\n // Tab already exists - just switch to it\n setActiveTerminalTab(activeSessionId);\n setTerminalCollapsed(false);\n } else {\n // Need to open a new tab for this session\n const url = session.ttydUrl || null;\n const newTab: TerminalTab = {\n sessionId: session.id,\n name: session.name,\n terminalUrl: url,\n loading: !url,\n error: null,\n };\n setTerminalTabs((prev) => [...prev, newTab]);\n setActiveTerminalTab(activeSessionId);\n setTerminalCollapsed(false);\n\n // If no URL available, start the terminal\n if (!url) {\n startSessionTerminal({ variables: { id: session.id } })\n .then((result) => {\n const newUrl = result.data?.startSessionTerminal?.ttydUrl || null;\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === session.id\n ? {\n ...t,\n terminalUrl: newUrl,\n loading: false,\n error: newUrl\n ? null\n : tr(\n 'sessions.terminalStartedNoUrl',\n 'Terminal started but no URL was returned. Try restarting.'\n ),\n }\n : t\n )\n );\n })\n .catch((err) => {\n const message =\n err instanceof Error\n ? err.message\n : tr('sessions.failedStartTerminal', 'Failed to start terminal');\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === session.id ? { ...t, loading: false, error: message } : t\n )\n );\n });\n }\n }\n }, [activeSessionId, sessions, terminalTabs, uiStateLoaded, startSessionTerminal]);\n\n // Filter sessions\n const filteredSessions = useMemo(() => {\n return sessions.filter((session) => {\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n session.name.toLowerCase().includes(query) ||\n session.command?.toLowerCase().includes(query) ||\n session.workingDirectory?.toLowerCase().includes(query);\n if (!matchesSearch) return false;\n }\n if (vibeFilter === 'none' && session.vibeId) return false;\n return true;\n });\n }, [sessions, searchQuery, vibeFilter]);\n\n // ── Bulk Selection ───────────────────────────────────────────────────\n const toggleSessionSelection = useCallback((sessionId: string) => {\n setSelectedSessionIds((prev) => {\n const next = new Set(prev);\n if (next.has(sessionId)) {\n next.delete(sessionId);\n } else {\n next.add(sessionId);\n }\n return next;\n });\n }, []);\n\n const selectAllSessions = useCallback(() => {\n setSelectedSessionIds(new Set(filteredSessions.map((s) => s.id)));\n }, [filteredSessions]);\n\n const deselectAllSessions = useCallback(() => {\n setSelectedSessionIds(new Set());\n }, []);\n\n const isAllSelected =\n filteredSessions.length > 0 && selectedSessionIds.size === filteredSessions.length;\n const hasSelection = selectedSessionIds.size > 0;\n\n // ── Bulk Actions ─────────────────────────────────────────────────────\n const handleBulkStop = useCallback(async () => {\n if (selectedSessionIds.size === 0) return;\n setBulkLoading(true);\n try {\n const result = await bulkStopSessions({ variables: { ids: Array.from(selectedSessionIds) } });\n const data = result.data?.bulkStopSessions;\n setSuccessMessage(\n `${tr('sessions.stopped', 'Stopped')} ${data?.succeeded ?? 0} ${tr('nav.sessions', 'sessions')}${\n data?.failed ? `, ${data.failed} ${tr('sessions.failed', 'failed')}` : ''\n }`\n );\n setSelectedSessionIds(new Set());\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n setSuccessMessage(\n `${tr('sessions.errorPrefix', 'Error')}: ${\n err instanceof Error ? err.message : tr('sessions.failedStop', 'Failed to stop sessions')\n }`\n );\n setTimeout(() => setSuccessMessage(null), 5000);\n } finally {\n setBulkLoading(false);\n }\n }, [selectedSessionIds, bulkStopSessions, tr]);\n\n const handleBulkRestart = useCallback(async () => {\n if (selectedSessionIds.size === 0) return;\n setBulkLoading(true);\n try {\n const result = await bulkRestartSessions({\n variables: { ids: Array.from(selectedSessionIds) },\n });\n const data = result.data?.bulkRestartSessions;\n setSuccessMessage(\n `${tr('sessions.restarted', 'Restarted')} ${data?.succeeded ?? 0} ${tr('nav.sessions', 'sessions')}${\n data?.failed ? `, ${data.failed} ${tr('sessions.failed', 'failed')}` : ''\n }`\n );\n setSelectedSessionIds(new Set());\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n setSuccessMessage(\n `${tr('sessions.errorPrefix', 'Error')}: ${\n err instanceof Error\n ? err.message\n : tr('sessions.failedRestart', 'Failed to restart sessions')\n }`\n );\n setTimeout(() => setSuccessMessage(null), 5000);\n } finally {\n setBulkLoading(false);\n }\n }, [selectedSessionIds, bulkRestartSessions, tr]);\n\n const handleBulkTerminate = useCallback(async () => {\n if (selectedSessionIds.size === 0) return;\n if (\n !confirm(\n `${tr('sessions.confirmRemovePrefix', 'Are you sure you want to remove')} ${selectedSessionIds.size} ${tr('nav.sessions', 'session(s)')}? ${tr('sessions.confirmRemoveSuffix', 'This cannot be undone.')}`\n )\n )\n return;\n setBulkLoading(true);\n try {\n const result = await bulkTerminateSessions({\n variables: { ids: Array.from(selectedSessionIds) },\n });\n const data = result.data?.bulkTerminateSessions;\n setSuccessMessage(\n `${tr('sessions.removed', 'Removed')} ${data?.succeeded ?? 0} ${tr('nav.sessions', 'sessions')}${\n data?.failed ? `, ${data.failed} ${tr('sessions.failed', 'failed')}` : ''\n }`\n );\n setSelectedSessionIds(new Set());\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n setSuccessMessage(\n `${tr('sessions.errorPrefix', 'Error')}: ${\n err instanceof Error\n ? err.message\n : tr('sessions.failedRemove', 'Failed to remove sessions')\n }`\n );\n setTimeout(() => setSuccessMessage(null), 5000);\n } finally {\n setBulkLoading(false);\n }\n }, [selectedSessionIds, bulkTerminateSessions, tr]);\n\n const handleExportSessions = useCallback(\n async (format: 'CSV' | 'JSON' = 'CSV') => {\n setIsExporting(true);\n try {\n const result = await exportSessions({\n variables: {\n input: {\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'RUNNING' | 'STOPPED' | 'STARTING' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as 'TMUX' | 'TERMINAL' | 'SSH' | 'SCRIPT' | 'CUSTOM')\n : undefined,\n vibeId: vibeFilter !== 'all' && vibeFilter !== 'none' ? vibeFilter : undefined,\n agentId: agentFilter !== 'all' ? agentFilter : undefined,\n },\n format,\n },\n },\n });\n const exportData = result.data?.exportSessions;\n if (!exportData) return;\n\n const mimeType = format === 'CSV' ? 'text/csv' : 'application/json';\n const blob = new Blob([exportData.data], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `sessions-export-${new Date().toISOString().slice(0, 10)}.${format.toLowerCase()}`;\n a.click();\n URL.revokeObjectURL(url);\n setSuccessMessage(\n tr('sessions.exportSuccess', 'Exported {{count}} sessions as {{format}}', {\n count: exportData.count,\n format,\n })\n );\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n setSuccessMessage(\n `${tr('sessions.errorPrefix', 'Error')}: ${\n err instanceof Error\n ? err.message\n : tr('sessions.failedExport', 'Failed to export sessions')\n }`\n );\n setTimeout(() => setSuccessMessage(null), 5000);\n } finally {\n setIsExporting(false);\n }\n },\n [exportSessions, statusFilter, typeFilter, vibeFilter, agentFilter, tr]\n );\n\n // ── Single Session Actions ───────────────────────────────────────────\n const handleSessionSelect = (session: Session) => {\n navigate(`/sessions/${session.id}`);\n };\n\n const handleCreateSession = () => {\n setCreateError(null);\n setShowCreateForm(true);\n };\n\n const handleSubmitCreateSession = async () => {\n const name = sessionNameRef.current?.value?.trim();\n const vibeId = formVibeId;\n const selectedAgentId = formAgentId;\n const type = formType;\n const command = sessionCommandRef.current?.value?.trim();\n const autoStart = sessionAutoStartRef.current?.checked ?? false;\n\n if (!name) {\n setCreateError(tr('sessions.nameRequired', 'Name is required'));\n return;\n }\n\n // Multi-agent: a vibe has 0..N attached agents. Pick the user's selected\n // agent when set; otherwise auto-pick when the vibe has exactly one\n // attached agent; otherwise force an explicit choice.\n const selectedVibe = cachedVibes.find((v) => v.id === vibeId);\n const candidateAgents = selectedVibe?.agents ?? [];\n let agentId: string | null = null;\n if (selectedAgentId && (!vibeId || candidateAgents.some((a) => a.id === selectedAgentId))) {\n agentId = selectedAgentId;\n } else if (candidateAgents.length === 1) {\n agentId = candidateAgents[0]?.id ?? null;\n }\n if (!agentId) {\n const message =\n vibeId && candidateAgents.length === 0\n ? tr(\n 'sessions.noAgentAttached',\n 'This vibe has no agents attached. Attach one from the Vibe → Agents tab before creating sessions.'\n )\n : vibeId && candidateAgents.length > 1\n ? tr(\n 'sessions.pickAgentFromVibe',\n 'This vibe has multiple agents attached. Pick one before creating the session.'\n )\n : tr('sessions.agentRequired', 'Please select an agent for this session.');\n setCreateError(message);\n return;\n }\n\n // Block session create when the agent's live deep-health probe is\n // not green — without this gate, the create succeeds (PENDING row\n // gets written) but the subsequent terminal-start step fails with\n // \"Terminal started but no tunnel URL\". Surface the actual probe\n // error inline instead of letting the user pile up dead PENDING\n // sessions against an unreachable agent.\n const probeRow = agentHealthByIdRef.get(agentId);\n if (probeRow && !probeRow.healthy) {\n setCreateError(\n tr(\n 'sessions.agentNotHealthy',\n 'The selected agent isn\\u2019t fully reachable right now. Please wait for the agent badge to turn green and retry.'\n ) + (probeRow.error ? ` (${probeRow.error})` : '')\n );\n return;\n }\n\n setCreateLoading(true);\n setCreateError(null);\n try {\n const externalSessionName = sessionExternalNameRef.current?.value?.trim() || undefined;\n const result = await createSession({\n variables: {\n input: {\n name,\n type: (type || 'TMUX') as\n | 'TMUX'\n | 'WEZTERM'\n | 'ZELLIJ'\n | 'TERMINAL'\n | 'SSH'\n | 'SCRIPT'\n | 'CUSTOM',\n command: command || undefined,\n vibeId: vibeId || undefined,\n agentId,\n autoStart,\n externalSessionName,\n },\n },\n });\n const newSessionId = result.data?.createSession?.id;\n setShowCreateForm(false);\n setSuccessMessage(tr('sessions.createdSuccess', 'Session created successfully'));\n if (newSessionId) await flushTags(newSessionId);\n setTimeout(() => setSuccessMessage(null), 3000);\n\n if (viewMode === 'terminal' && result.data?.createSession) {\n const newSession: Session = {\n id: result.data.createSession.id,\n workspaceId: result.data.createSession.workspaceId,\n vibeId: result.data.createSession.vibeId || null,\n agentId: result.data.createSession.agentId || null,\n name: result.data.createSession.name,\n type: result.data.createSession.type as Session['type'],\n status: result.data.createSession.status as Session['status'],\n command: result.data.createSession.command || null,\n workingDirectory: result.data.createSession.workingDirectory || null,\n environmentVariables:\n (result.data.createSession.environmentVariables as Record<string, string>) || {},\n autoStart: result.data.createSession.autoStart,\n providerSessionName: result.data.createSession.providerSessionName || undefined,\n providerWindowName: result.data.createSession.providerWindowId || undefined,\n pid: result.data.createSession.pid || null,\n exitCode: result.data.createSession.exitCode || null,\n lastOutput: result.data.createSession.lastOutput || null,\n ttydUrl: result.data.createSession.ttydUrl || null,\n metadata: (result.data.createSession.metadata as Record<string, unknown>) || {},\n createdAt: result.data.createSession.createdAt,\n updatedAt: result.data.createSession.updatedAt,\n };\n handleOpenTerminal(newSession);\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowCreateForm(false);\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n setCreateError(\n err instanceof Error ? err.message : tr('sessions.failedCreate', 'Failed to create session')\n );\n } finally {\n setCreateLoading(false);\n }\n };\n\n const handleStartSession = async (session: Session) => {\n try {\n await startSession({ variables: { id: session.id } });\n onSessionStart?.({\n id: session.id,\n name: session.name,\n type: session.type,\n status: 'RUNNING',\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n console.error('Failed to start session:', err);\n }\n };\n\n const handleStopSession = async (session: Session) => {\n try {\n await stopSession({ variables: { id: session.id } });\n } catch (err) {\n console.error('Failed to stop session:', err);\n }\n };\n\n const handleRestartSession = async (session: Session) => {\n try {\n await restartSession({ variables: { id: session.id } });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n console.error('Failed to restart session:', err);\n }\n };\n\n const handleTerminateSession = async (session: Session) => {\n try {\n await terminateSession({ variables: { id: session.id } });\n } catch (err) {\n console.error('Failed to terminate session:', err);\n }\n };\n\n const getTerminalUrl = useCallback((session: Session): string | null => {\n return session.ttydUrl || null;\n }, []);\n\n // Share session handler\n const handleShareSession = useCallback((session: Session) => {\n setShareSession(session);\n }, []);\n\n const handleOpenTerminal = useCallback(\n async (session: Session) => {\n const existingTab = terminalTabs.find((t) => t.sessionId === session.id);\n if (existingTab?.terminalUrl) {\n setActiveTerminalTab(session.id);\n setTerminalCollapsed(false);\n return;\n }\n\n const url = getTerminalUrl(session);\n\n if (url) {\n const newTab: TerminalTab = {\n sessionId: session.id,\n name: session.name,\n terminalUrl: url,\n loading: false,\n error: null,\n };\n setTerminalTabs((prev) => {\n const filtered = prev.filter((t) => t.sessionId !== session.id);\n return [...filtered, newTab];\n });\n setActiveTerminalTab(session.id);\n setTerminalCollapsed(false);\n return;\n }\n\n const loadingTab: TerminalTab = {\n sessionId: session.id,\n name: session.name,\n terminalUrl: null,\n loading: true,\n error: null,\n };\n setTerminalTabs((prev) => {\n const filtered = prev.filter((t) => t.sessionId !== session.id);\n return [...filtered, loadingTab];\n });\n setActiveTerminalTab(session.id);\n setTerminalCollapsed(false);\n\n try {\n const result = await startSessionTerminal({ variables: { id: session.id } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n const remainingTabs = terminalTabs.filter((t) => t.sessionId !== session.id);\n setTerminalTabs(remainingTabs);\n if (activeTerminalTab === session.id) {\n setActiveTerminalTab(\n remainingTabs.length > 0 ? remainingTabs[remainingTabs.length - 1].sessionId : null\n );\n }\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n // Apply the same browser-host normalization we use for list rows\n // so a `host.docker.internal`/`localhost` URL falls back to the\n // agent's known tunnel host where possible. The mutation response\n // doesn't fetch the agent inline (SessionFields fragment is flat),\n // so we look up the tunnelUrl from the already-loaded session row.\n const rawUrl = result.data?.startSessionTerminal?.ttydUrl || null;\n const sessionRow = sessions.find((s) => s.id === session.id);\n const tunnelHint = sessionRow?.agent?.tunnelUrl ?? null;\n const newUrl = normalizeSessionTerminalUrl(rawUrl, tunnelHint);\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === session.id\n ? {\n ...t,\n terminalUrl: newUrl,\n loading: false,\n error: newUrl\n ? null\n : tr(\n 'sessions.terminalStartedNoTunnelUrl',\n 'Terminal started but no tunnel URL was returned. Try restarting.'\n ),\n }\n : t\n )\n );\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n const remainingTabs = terminalTabs.filter((t) => t.sessionId !== session.id);\n setTerminalTabs(remainingTabs);\n if (activeTerminalTab === session.id) {\n setActiveTerminalTab(\n remainingTabs.length > 0 ? remainingTabs[remainingTabs.length - 1].sessionId : null\n );\n }\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n const message =\n err instanceof Error\n ? err.message\n : tr('sessions.failedStartTerminal', 'Failed to start terminal');\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === session.id ? { ...t, loading: false, error: message } : t\n )\n );\n }\n },\n [activeTerminalTab, terminalTabs, getTerminalUrl, startSessionTerminal]\n );\n\n useEffect(() => {\n if (!uiStateLoaded || !activeTerminalTab) return;\n if (terminalTabs.some((tab) => tab.sessionId === activeTerminalTab)) return;\n\n const session = sessions.find((item) => item.id === activeTerminalTab);\n if (session) {\n void handleOpenTerminal(session);\n }\n }, [activeTerminalTab, handleOpenTerminal, sessions, terminalTabs, uiStateLoaded]);\n\n const handleCloseTerminalTab = useCallback(\n (sessionId: string) => {\n setTerminalTabs((prev) => prev.filter((t) => t.sessionId !== sessionId));\n if (activeTerminalTab === sessionId) {\n setActiveTerminalTab(() => {\n const remaining = terminalTabs.filter((t) => t.sessionId !== sessionId);\n return remaining.length > 0 ? remaining[remaining.length - 1].sessionId : null;\n });\n }\n },\n [activeTerminalTab, terminalTabs]\n );\n\n const handleOpenTerminalExternal = useCallback(\n (sessionId: string) => {\n const tab = terminalTabs.find((t) => t.sessionId === sessionId);\n if (tab?.terminalUrl) window.open(tab.terminalUrl, '_blank');\n },\n [terminalTabs]\n );\n\n const handleRenameSession = useCallback(\n async (sessionId: string, newName: string) => {\n try {\n await updateSession({ variables: { id: sessionId, input: { name: newName } } });\n setTerminalTabs((prev) =>\n prev.map((t) => (t.sessionId === sessionId ? { ...t, name: newName } : t))\n );\n } catch (err) {\n console.error('Failed to rename session:', err);\n }\n },\n [updateSession]\n );\n\n const handleStopTerminal = useCallback(\n async (sessionId: string) => {\n try {\n await stopSessionTerminal({ variables: { id: sessionId } });\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId\n ? {\n ...t,\n terminalUrl: null,\n error: tr('sessions.terminalStopped', 'Terminal stopped'),\n }\n : t\n )\n );\n } catch (err) {\n console.error('Failed to stop terminal:', err);\n }\n },\n [stopSessionTerminal]\n );\n\n const handleRestartTerminal = useCallback(\n async (sessionId: string) => {\n const previousTab = terminalTabs.find((t) => t.sessionId === sessionId) || null;\n\n try {\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId ? { ...t, loading: true, error: null, terminalUrl: null } : t\n )\n );\n const result = await restartSessionTerminal({ variables: { id: sessionId } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId\n ? {\n ...t,\n loading: false,\n error: null,\n terminalUrl: previousTab?.terminalUrl ?? t.terminalUrl,\n }\n : t\n )\n );\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n const newUrl = result.data?.restartSessionTerminal?.ttydUrl || null;\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId\n ? {\n ...t,\n terminalUrl: newUrl,\n loading: false,\n error: newUrl\n ? null\n : tr(\n 'sessions.terminalRestartedNoUrl',\n 'Terminal restarted but no URL returned.'\n ),\n }\n : t\n )\n );\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId\n ? {\n ...t,\n loading: false,\n error: null,\n terminalUrl: previousTab?.terminalUrl ?? t.terminalUrl,\n }\n : t\n )\n );\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n return;\n }\n\n const message =\n err instanceof Error\n ? err.message\n : tr('sessions.failedRestartTerminal', 'Failed to restart terminal');\n setTerminalTabs((prev) =>\n prev.map((t) =>\n t.sessionId === sessionId ? { ...t, loading: false, error: message } : t\n )\n );\n }\n },\n [restartSessionTerminal, terminalTabs]\n );\n\n // Toggle immersive fullscreen mode\n const handleToggleImmersiveFullscreen = useCallback(() => {\n setIsImmersiveFullscreen((prev) => !prev);\n }, []);\n\n // Stats\n const stats = {\n total: sessions.length,\n running: sessions.filter((s) => s.status === 'RUNNING').length,\n stopped: sessions.filter((s) => s.status === 'STOPPED').length,\n };\n\n // Guard states rendered inside MotionPage to prevent unmount/remount animation replay\n if (permissionsLoading) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('sessions.loadingPermissions', 'Loading permissions...')} />\n </MotionPage>\n );\n }\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canListSessions) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'sessions.accessDeniedDescription',\n \"You don't have permission to view sessions.\"\n )}\n details={tr('sessions.requiredPermission', 'Required permission: session:list')}\n />\n </MotionPage>\n );\n }\n\n const isInitialLoad = loading && !previousData && sessions.length === 0;\n if (isInitialLoad) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('sessions.loading', 'Loading sessions...')} />\n </MotionPage>\n );\n }\n\n if (error && sessions.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('sessions.failedLoad', 'Failed to load sessions')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n // ── Bulk Action Bar ──────────────────────────────────────────────────\n const renderBulkActionBar = () => {\n if (!hasSelection || viewMode === 'terminal') return null;\n return (\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-3 px-4 py-2 bg-action-primary-bg/10 border border-action-primary-bg/20 rounded-lg\">\n <span className=\"text-sm font-medium text-text-primary\">\n {tr(\n selectedSessionIds.size === 1\n ? 'sessions.selectionCountSingle'\n : 'sessions.selectionCountPlural',\n selectedSessionIds.size === 1\n ? '{{count}} session selected'\n : '{{count}} sessions selected',\n { count: selectedSessionIds.size }\n )}\n </span>\n <div className=\"flex items-center gap-1.5 sm:ml-auto flex-wrap\">\n <button\n type=\"button\"\n onClick={handleBulkStop}\n disabled={bulkLoading}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <Square className=\"size-3\" /> {tr('sessions.stop', 'Stop')}\n </button>\n <button\n type=\"button\"\n onClick={handleBulkRestart}\n disabled={bulkLoading}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <RotateCcw className=\"size-3\" /> {tr('sessions.restart', 'Restart')}\n </button>\n <button\n type=\"button\"\n onClick={handleBulkTerminate}\n disabled={bulkLoading}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-status-error-text bg-status-error-bg border border-status-error-text/20 rounded-md hover:bg-status-error-bg/80 transition-colors disabled:opacity-50\"\n >\n <Trash2 className=\"size-3\" /> {tr('sessions.remove', 'Remove')}\n </button>\n <button\n type=\"button\"\n onClick={deselectAllSessions}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n <XSquare className=\"size-3\" /> {tr('sessions.deselect', 'Deselect')}\n </button>\n </div>\n </div>\n );\n };\n\n // Terminal-first view\n if (viewMode === 'terminal') {\n return (\n <MotionPage className=\"flex flex-col bg-bg-surface h-screen\">\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 px-4 py-2 border-b border-border-default bg-bg-sunken\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-sm font-semibold text-text-primary\">\n {tr('nav.sessions', 'Sessions')}\n </h1>\n <span className=\"text-xs text-text-muted\">\n {tr('sessions.runningSummary', '{{running}} running / {{total}} total', {\n running: stats.running,\n total: stats.total,\n })}\n </span>\n </div>\n <div className=\"flex w-full flex-wrap items-center gap-2 sm:w-auto sm:justify-end\">\n <button\n type=\"button\"\n onClick={() => checkSessionHealth().then(() => refetch())}\n className=\"p-1.5 text-text-secondary hover:bg-bg-surface rounded transition-colors\"\n title={tr('sessions.checkHealth', 'Check session health')}\n >\n <Activity className=\"size-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => {\n refetch();\n void refetchProbes();\n }}\n disabled={loading}\n className=\"p-1.5 text-text-secondary hover:bg-bg-surface rounded transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`size-3.5 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <div className=\"flex items-center gap-0.5 rounded bg-bg-surface p-0.5\">\n <button\n type=\"button\"\n onClick={() => setViewMode('terminal')}\n className=\"p-1.5 rounded bg-bg-sunken shadow-sm\"\n title={tr('sessions.terminalViewTitle', 'Terminal view')}\n >\n <Monitor className=\"size-3.5 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className=\"p-1.5 rounded hover:bg-bg-sunken/50\"\n title={tr('sessions.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"size-3.5 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className=\"p-1.5 rounded hover:bg-bg-sunken/50\"\n title={tr('sessions.listViewTitle', 'List view')}\n >\n <List className=\"size-3.5 text-text-secondary\" />\n </button>\n </div>\n </div>\n </div>\n\n {/* Agent Notifications Banner */}\n <div className=\"px-4 pt-3\">\n <SessionNotifications showConnectionStatus={true} maxVisible={3} />\n </div>\n\n <TerminalPanel\n tabs={terminalTabs}\n activeTabId={activeTerminalTab}\n onTabSelect={setActiveTerminalTab}\n onTabClose={handleCloseTerminalTab}\n onOpenExternal={handleOpenTerminalExternal}\n collapsed={false}\n sessions={filteredSessions}\n onOpenTerminal={handleOpenTerminal}\n onRetryTerminal={handleOpenTerminal}\n onRenameSession={handleRenameSession}\n onStopTerminal={handleStopTerminal}\n onRestartTerminal={handleRestartTerminal}\n onCreateSession={handleCreateSession}\n fullScreen={true}\n isImmersiveFullscreen={isImmersiveFullscreen}\n onToggleImmersiveFullscreen={handleToggleImmersiveFullscreen}\n contextSwitcher={undefined}\n />\n\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n {showCreateForm && renderCreateModal()}\n {renderReconnectModal()}\n </MotionPage>\n );\n }\n\n // Grid/List view\n return (\n <MotionPage className=\"flex flex-col bg-bg-surface min-h-screen\">\n <div className=\"flex-1 overflow-y-auto space-y-6 p-4 sm:p-6\">\n <PageHeader\n title={tr('nav.sessions', 'Sessions')}\n description={tr('sessions.pageDescription', '{{running}} of {{total}} sessions running', {\n running: stats.running,\n total: stats.total,\n })}\n actions={\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => checkSessionHealth().then(() => refetch())}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors\"\n title={tr('sessions.checkHealth', 'Check session health')}\n >\n <Activity className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('sessions.refreshTitle', 'Refresh sessions')}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <SaveToFilesButton\n label={tr('sessions.export', 'Export')}\n disabled={isExporting}\n testId=\"sessions-export\"\n defaultFolderPath=\"/Vibecontrols/Sessions\"\n source={{\n kind: 'blob',\n filename: `sessions-export-${new Date().toISOString().slice(0, 10)}.csv`,\n mimeType: 'text/csv',\n getBlob: async () => {\n const result = await exportSessions({\n variables: {\n input: {\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'RUNNING' | 'STOPPED' | 'STARTING' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as 'TMUX' | 'TERMINAL' | 'SSH' | 'SCRIPT' | 'CUSTOM')\n : undefined,\n vibeId:\n vibeFilter !== 'all' && vibeFilter !== 'none'\n ? vibeFilter\n : undefined,\n agentId: agentFilter !== 'all' ? agentFilter : undefined,\n },\n format: 'CSV',\n },\n },\n });\n const exportData = result.data?.exportSessions;\n if (!exportData) return null;\n return { blob: new Blob([exportData.data], { type: 'text/csv' }) };\n },\n }}\n onDownload={() => handleExportSessions('CSV')}\n />\n <PermissionButton permission={VIBE_PERMISSIONS.SESSION_CREATE}>\n <button\n type=\"button\"\n onClick={() => setShowReconnectModal(true)}\n className=\"flex items-center gap-2 px-3 py-2 text-text-primary border border-border-default rounded-md hover:bg-bg-sunken transition-colors\"\n data-testid=\"sessions-reconnect-button\"\n title={tr(\n 'sessions.reconnectTooltip',\n 'Reconnect orphan tmux/wezterm/zellij sessions left over from a previous agent run'\n )}\n >\n <RefreshCw className=\"size-4\" />\n {tr('sessions.reconnect', 'Reconnect')}\n </button>\n </PermissionButton>\n <PermissionButton permission={VIBE_PERMISSIONS.SESSION_CREATE}>\n <button\n type=\"button\"\n onClick={handleCreateSession}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('sessions.newSession', 'New Session')}\n </button>\n </PermissionButton>\n </div>\n }\n />\n\n {/* Agent Notifications Banner */}\n <SessionNotifications showConnectionStatus={true} maxVisible={3} />\n\n {/* Filters Bar */}\n <div data-tour=\"sessions-filters\" className=\"space-y-3\">\n {/* Row 1: Search + View Toggle */}\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('sessions.searchPlaceholder', 'Search sessions...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setViewMode('terminal')}\n className=\"p-1.5 sm:p-2 rounded hover:bg-bg-surface/50\"\n title={tr('sessions.terminalViewTitle', 'Terminal view')}\n >\n <Monitor className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${(viewMode as ViewMode) === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('sessions.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${(viewMode as ViewMode) === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('sessions.listViewTitle', 'List view')}\n >\n <List className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Filters - wrap on all screens */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('sessions.filterStatus', 'Status')}\n value={statusFilter}\n options={statusOptions}\n onChange={setStatusFilter}\n />\n <FilterDropdown\n label={tr('sessions.filterType', 'Type')}\n value={typeFilter}\n options={typeOptions}\n onChange={setTypeFilter}\n />\n <FilterDropdown\n label={tr('sessions.filterVibe', 'Vibe')}\n value={vibeFilter}\n options={vibeOptions}\n onChange={setVibeFilter}\n />\n <FilterDropdown\n label={tr('sessions.filterAgent', 'Agent')}\n value={agentFilter}\n options={agentOptions}\n onChange={setAgentFilter}\n />\n </div>\n </div>\n\n {/* Quick Actions & Select All */}\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-2 flex-wrap\">\n <button\n type=\"button\"\n onClick={() => setStatusFilter('RUNNING')}\n className={`flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${\n statusFilter === 'RUNNING'\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n <Play className=\"size-3\" /> {tr('sessions.running', 'Running')} ({stats.running})\n </button>\n <button\n type=\"button\"\n onClick={() => setStatusFilter('STOPPED')}\n className={`flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${\n statusFilter === 'STOPPED'\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n <Square className=\"size-3\" /> {tr('sessions.stoppedState', 'Stopped')} ({stats.stopped})\n </button>\n <div className=\"sm:ml-auto flex items-center gap-2\">\n {filteredSessions.length > 0 && (\n <button\n type=\"button\"\n onClick={isAllSelected ? deselectAllSessions : selectAllSessions}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n <CheckSquare className=\"size-3.5\" />\n {isAllSelected\n ? tr('sessions.deselectAll', 'Deselect All')\n : tr('sessions.selectAll', 'Select All')}\n </button>\n )}\n </div>\n </div>\n\n {/* Bulk Action Bar */}\n {renderBulkActionBar()}\n\n {/* Results Count */}\n {searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n vibeFilter !== 'all' ||\n agentFilter !== 'all' ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('sessions.showing', 'Showing')} {filteredSessions.length} {tr('sessions.of', 'of')}{' '}\n {sessions.length} {tr('nav.sessions', 'sessions')}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n ({tr('sessions.refreshing', 'refreshing...')})\n </span>\n )}\n </p>\n ) : null}\n\n {/* Sessions Display */}\n <AnimatePresence mode=\"wait\">\n {filteredSessions.length > 0 ? (\n viewMode === 'grid' ? (\n <MotionList\n key=\"grid-view\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredSessions.map((session) => (\n <MotionItem key={session.id}>\n <SessionCard\n session={session}\n onOpen={handleSessionSelect}\n onStart={handleStartSession}\n onStop={handleStopSession}\n onRestart={handleRestartSession}\n onOpenTerminal={handleOpenTerminal}\n onShare={handleShareSession}\n selected={selectedSessionIds.has(session.id)}\n onToggleSelect={() => toggleSessionSelection(session.id)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <MotionItem key=\"list-view\">\n <SessionList\n sessions={filteredSessions}\n onOpen={handleSessionSelect}\n onStart={handleStartSession}\n onStop={handleStopSession}\n onRestart={handleRestartSession}\n onTerminate={handleTerminateSession}\n onOpenTerminal={handleOpenTerminal}\n onShare={handleShareSession}\n selectedIds={selectedSessionIds}\n onToggleSelect={toggleSessionSelection}\n onSelectAll={selectAllSessions}\n onDeselectAll={deselectAllSessions}\n />\n </MotionItem>\n )\n ) : (\n <div className=\"space-y-4\">\n <AgentSetupBanner variant=\"compact\" dismissKey=\"vibecontrols-sessions-setup-banner\" />\n <EmptyState\n icon={<Terminal className=\"size-8 text-text-secondary\" />}\n title={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n vibeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('sessions.emptyFound', 'No sessions found')\n : tr('sessions.emptyConfigured', 'No sessions configured')\n }\n description={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n vibeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('sessions.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'sessions.emptyDescription',\n 'Sessions require an active agent. Install the CLI agent first, then start a session.'\n )\n }\n action={\n !searchQuery &&\n statusFilter === 'all' &&\n typeFilter === 'all' &&\n vibeFilter === 'all' &&\n agentFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n {canCreateSession && (\n <button\n type=\"button\"\n onClick={handleCreateSession}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" /> {tr('sessions.newSession', 'New Session')}\n </button>\n )}\n <GetHelpButton prompt=\"What is a Session in VibeControls and how do I start one?\" />\n </div>\n ) : undefined\n }\n />\n </div>\n )}\n </AnimatePresence>\n\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n </div>\n\n {/* Tabbed Terminal Panel - docked at bottom */}\n {terminalTabs.length > 0 && (\n <TerminalPanel\n tabs={terminalTabs}\n activeTabId={activeTerminalTab}\n onTabSelect={setActiveTerminalTab}\n onTabClose={handleCloseTerminalTab}\n onOpenExternal={handleOpenTerminalExternal}\n onToggleCollapse={() => setTerminalCollapsed((prev) => !prev)}\n collapsed={terminalCollapsed}\n sessions={filteredSessions}\n onOpenTerminal={handleOpenTerminal}\n onRetryTerminal={handleOpenTerminal}\n onRenameSession={handleRenameSession}\n onStopTerminal={handleStopTerminal}\n onRestartTerminal={handleRestartTerminal}\n heightClass=\"h-[280px] sm:h-[380px]\"\n isImmersiveFullscreen={isImmersiveFullscreen}\n onToggleImmersiveFullscreen={handleToggleImmersiveFullscreen}\n contextSwitcher={undefined}\n />\n )}\n\n {showCreateForm && renderCreateModal()}\n {renderReconnectModal()}\n\n {/* Share Session Dialog */}\n {shareSession && (\n <SessionShareDialog\n session={shareSession}\n isOpen={!!shareSession}\n onClose={() => setShareSession(null)}\n />\n )}\n\n <ConfirmDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title={getQuotaDialogContent('session').title}\n message={getQuotaDialogContent('session').message}\n confirmLabel=\"View Billing\"\n cancelLabel=\"Dismiss\"\n variant=\"warning\"\n />\n </MotionPage>\n );\n\n function renderReconnectModal() {\n if (!showReconnectModal) return null;\n const orphans = orphansData?.agentSessionOrphans ?? [];\n return (\n <div\n className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\"\n data-testid=\"sessions-reconnect-modal\"\n >\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-2xl max-h-[80vh] overflow-y-auto\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-1\">\n {tr('sessions.reconnectTitle', 'Reconnect orphan sessions')}\n </h2>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tr(\n 'sessions.reconnectDesc',\n 'Pick an agent to scan its host for tmux / wezterm / zellij sessions named vibe-* that are not currently tracked. Adopt one to start a web terminal and bring it back into the UI.'\n )}\n </p>\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.agent', 'Agent')}\n </label>\n <SearchableSelect\n options={createAgentOptions}\n value={reconnectAgentId}\n onChange={(id) => {\n setReconnectAgentId(id);\n setReconnectError(null);\n if (id) void discoverOrphans({ variables: { agentId: id } });\n }}\n placeholder={tr('sessions.selectAgent', 'Select an agent')}\n searchPlaceholder={tr('sessions.searchAgents', 'Search agents...')}\n clearable\n />\n </div>\n {reconnectError && (\n <div\n className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\"\n data-testid=\"sessions-reconnect-error\"\n >\n {reconnectError}\n </div>\n )}\n {reconnectAgentId && (\n <div data-testid=\"sessions-reconnect-list\" className=\"space-y-2\">\n {orphansLoading ? (\n <div className=\"text-sm text-text-secondary\">\n {tr('sessions.scanningHost', 'Scanning host...')}\n </div>\n ) : orphans.length === 0 ? (\n <div className=\"text-sm text-text-secondary\">\n {tr('sessions.noOrphans', 'No orphan vibe-* sessions found on this agent.')}\n </div>\n ) : (\n orphans.map(\n (o: {\n externalName: string;\n provider: string;\n windows: number;\n attached: boolean;\n createdAt?: string | null;\n }) => (\n <div\n key={o.externalName}\n className=\"flex items-center justify-between gap-3 p-3 border border-border-default rounded-md bg-bg-sunken/40\"\n data-testid=\"sessions-reconnect-row\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"font-mono text-sm text-text-primary break-all\">\n {o.externalName}\n </div>\n <div className=\"text-xs text-text-tertiary mt-0.5\">\n {o.provider} · {o.windows} window{o.windows !== 1 ? 's' : ''}\n {o.attached ? ' · attached' : ''}\n {o.createdAt ? ` · created ${o.createdAt}` : ''}\n </div>\n </div>\n <button\n type=\"button\"\n disabled={reconnectAdoptingName === o.externalName}\n onClick={async () => {\n setReconnectAdoptingName(o.externalName);\n setReconnectError(null);\n try {\n await adoptAgentSession({\n variables: {\n agentId: reconnectAgentId,\n externalName: o.externalName,\n name: o.externalName,\n },\n });\n // Refresh orphan list so the adopted one disappears.\n await discoverOrphans({\n variables: { agentId: reconnectAgentId },\n });\n } catch (e) {\n setReconnectError(e instanceof Error ? e.message : String(e));\n } finally {\n setReconnectAdoptingName(null);\n }\n }}\n className=\"shrink-0 px-3 py-1.5 text-xs bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n data-testid={`sessions-reconnect-adopt-${o.externalName}`}\n >\n {reconnectAdoptingName === o.externalName\n ? tr('sessions.adopting', 'Adopting…')\n : tr('sessions.adopt', 'Adopt')}\n </button>\n </div>\n )\n )\n )}\n </div>\n )}\n </div>\n <div className=\"flex justify-end gap-3 mt-6\">\n <button\n type=\"button\"\n onClick={() => {\n setShowReconnectModal(false);\n setReconnectError(null);\n }}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n {tr('common.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n function renderCreateModal() {\n return (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('sessions.newSession', 'New Session')}\n </h2>\n <div className=\"space-y-4\">\n {createError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {createError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.name', 'Name')}\n </label>\n <input\n aria-label=\"control\"\n ref={sessionNameRef}\n type=\"text\"\n placeholder={tr('sessions.namePlaceholder', 'dev-server')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.filterVibe', 'Vibe')}\n </label>\n <SearchableSelect\n options={createVibeOptions}\n value={formVibeId}\n onChange={setFormVibeId}\n placeholder={tr('sessions.noVibe', 'No vibe')}\n searchPlaceholder={tr('sessions.searchVibes', 'Search vibes...')}\n clearable\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.filterAgent', 'Agent')}\n </label>\n <SearchableSelect\n options={createAgentOptions}\n value={formAgentId}\n onChange={setFormAgentId}\n placeholder={tr('sessions.selectAgent', 'Select an agent')}\n searchPlaceholder={tr('sessions.searchAgents', 'Search agents...')}\n clearable\n />\n <p className=\"text-xs text-text-muted mt-1\">\n {tr(\n 'sessions.agentHint',\n \"Required when no vibe is selected. Overridden by the vibe's agent if a vibe is chosen.\"\n )}\n </p>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.filterType', 'Type')}\n </label>\n <SearchableSelect\n options={sessionTypeOptions}\n value={formType}\n onChange={setFormType}\n placeholder={tr('sessions.selectType', 'Select type')}\n searchPlaceholder={tr('sessions.searchTypes', 'Search types...')}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('sessions.command', 'Command')}\n </label>\n <input\n aria-label=\"control\"\n ref={sessionCommandRef}\n type=\"text\"\n placeholder={tr('sessions.commandPlaceholder', 'bun run dev')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <input\n aria-label=\"autoStart\"\n ref={sessionAutoStartRef}\n type=\"checkbox\"\n id=\"autoStart\"\n className=\"size-4 rounded border-border-default\"\n />\n <label htmlFor=\"autoStart\" className=\"text-sm text-text-primary\">\n {tr('sessions.autoStartWithVibe', 'Auto-start with vibe')}\n </label>\n </div>\n {(formType === 'TMUX' || formType === 'WEZTERM' || formType === 'ZELLIJ') && (\n <div\n className=\"rounded-md border border-border-default bg-bg-sunken/40\"\n data-testid=\"sessions-create-advanced\"\n >\n <button\n type=\"button\"\n onClick={() => setFormShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between px-3 py-2 text-sm font-medium text-text-primary\"\n aria-expanded={formShowAdvanced}\n >\n <span>{tr('sessions.advanced', 'Advanced options')}</span>\n <span className=\"text-text-tertiary text-xs\">{formShowAdvanced ? '▾' : '▸'}</span>\n </button>\n {formShowAdvanced && (\n <div className=\"px-3 pb-3 space-y-2\">\n <label className=\"block text-xs font-medium text-text-secondary\">\n {tr('sessions.attachExistingLabel', 'Attach to existing session (optional)')}\n </label>\n <input\n aria-label=\"control\"\n ref={sessionExternalNameRef}\n type=\"text\"\n data-testid=\"sessions-create-external-name\"\n placeholder={\n formType === 'WEZTERM'\n ? 'e.g. vibe-051c9c62 (existing wezterm workspace)'\n : formType === 'ZELLIJ'\n ? 'e.g. vibe-051c9c62 (existing zellij session)'\n : 'e.g. vibe-051c9c62 (existing tmux session)'\n }\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder font-mono\"\n />\n <p className=\"text-xs text-text-tertiary\">\n {tr(\n 'sessions.attachExistingHint',\n 'When set, the agent attaches to that existing session instead of creating a new one. Leave blank to spawn fresh.'\n )}\n </p>\n </div>\n )}\n </div>\n )}\n </div>\n <div className=\"border-t border-border-default pt-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('sessions.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_SESSION\" onDraftChange={setDraftTagIds} />\n </div>\n <div className=\"flex justify-end gap-3 mt-6\">\n <button\n type=\"button\"\n onClick={() => setShowCreateForm(false)}\n disabled={createLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSubmitCreateSession}\n disabled={createLoading}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {createLoading\n ? tr('sessions.creating', 'Creating...')\n : tr('sessions.createSession', 'Create Session')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n}\n"],"mappings":"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"}