@burdenoff/microfe-vibecontrols 2026.710.1 → 2026.714.1

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":"AgentDetailsPage.js","names":[],"sources":["../../src/pages/AgentDetailsPage.tsx"],"sourcesContent":["import { useState, useRef, useEffect, useCallback, useMemo } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { isNative, nativeCopyText, nativeToast } from '../utils/nativeBridge';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useParams } from 'react-router-dom';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { AgentBackupTab } from '@/components/agents/backup/AgentBackupTab';\nimport { ConnectionLogPanel } from '@/components/agents/ConnectionLogPanel';\nimport { AgentMaintenanceTab } from '@/components/agents/AgentMaintenanceTab';\nimport { AgentConnectionRecovery } from '@/components/agents/AgentConnectionRecovery';\nimport { AgentStorageConfig } from '@/components/agents/AgentStorageConfig';\nimport { AliasManager } from '@/components/aliases/AliasManager';\nimport {\n ArrowLeft,\n Database,\n Server,\n Play,\n Square,\n Settings,\n Trash2,\n Terminal,\n Zap,\n Cpu,\n HardDrive,\n Clock,\n CheckCircle2,\n RefreshCw,\n Eye,\n EyeOff,\n Key,\n Copy,\n Check,\n Info,\n Puzzle,\n Download,\n X,\n Loader2,\n Shield,\n Monitor,\n FileText,\n Tag,\n ScrollText,\n Command,\n Users,\n Plus,\n Edit3,\n Bot,\n Wrench,\n ArrowUpCircle,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatusBadge,\n EmptyState,\n ErrorState,\n QuotaExhaustedDialog,\n ConfirmDialog,\n ActionMenu,\n MotionTabPanel,\n PromptDialog,\n SearchInput,\n} from '@/components/shared';\nimport { Button, Checkbox, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { Tabs, type TabItem } from '@burdenoff/fe-libs/chrome';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { notify } from '@/lib/toast';\nimport {\n PluginUIPanel,\n type UIPluginOption,\n OpenInBrowserEditorButton,\n AgentStatusBadge,\n RemoteTaskPermissionsCard,\n GatewayAuthCard,\n ConfigureAgentDialog,\n CreateSessionDialog,\n} from '@/components/agents';\nimport { deriveAgentStatus, type AgentHealthProbe } from '@/utils/agentStatus';\nimport { LogViewer } from '@/components/logs/LogViewer';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { ProfilesPanel } from '@/components/agent-manager';\nimport { NotesTabPanel } from '@/components/notes';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useQueryBackedTab } from '@/hooks/useQueryBackedTab';\nimport {\n isQuotaExhaustedError,\n markQuotaHandledLocally,\n getQuotaDialogContent,\n} from '@/utils/quotaUtils';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport {\n type DiscoveredContribution,\n type MountTunnel,\n usePluginContributions,\n} from '@burdenoff/fe-libs/shared/plugins';\nimport { mintScopedIframeToken } from '@/services/iframeToken';\nimport { AddTabDialog } from '@/components/plugins/AddTabDialog';\nimport { PluginIframeMount } from '@/components/plugins/PluginIframeMount';\nimport {\n useGetAgentQuery,\n useUpdateAgentMutation,\n useDeleteAgentMutation,\n useCheckSessionHealthMutation,\n useAgentPluginsLazyQuery,\n useInstallAgentPluginMutation,\n useRemoveAgentPluginMutation,\n useUpdateAgentPluginMutation,\n useSetupAgentGatewayAuthMutation,\n useVibecontrolsAgentHealthProbesQuery,\n useVibecontrolsAgentHealthProbeStreamSubscription,\n useStartVibecontrolsAgentMutation,\n useStopVibecontrolsAgentMutation,\n useAgentSecretRevealLazyQuery,\n} from '@/generated/wspace-operations';\n\n// The Gateway Auth (OAuth2) card lives in\n// `@/components/agents/GatewayAuthCard` — extracted from this page.\n\n// `AgentPluginIframeBody` was the page-local helper that minted a\n// token + rendered an iframe without refresh / error handling. The\n// consolidated logic now lives in `@/components/plugins/PluginIframeMount`.\n\ntype AgentTab =\n | 'overview'\n | 'profiles'\n | 'security'\n | 'plugins'\n | 'sessions'\n | 'logs'\n | 'connection'\n | 'vibes'\n | 'notes'\n | 'aliases'\n | 'backup'\n | 'maintenance';\n\n// Module-scope so the reference is stable across renders (useMemo dep).\nconst AGENT_TAB_IDS: readonly AgentTab[] = [\n 'overview',\n 'profiles',\n 'security',\n 'plugins',\n 'sessions',\n 'logs',\n 'connection',\n 'vibes',\n 'notes',\n 'aliases',\n 'backup',\n 'maintenance',\n];\n\n/**\n * Agent details page - shows comprehensive information about a single agent\n */\nexport function AgentDetailsPage() {\n const { id } = useParams<{ id: string }>();\n const { basePath: basePathProp } = useVibeControls();\n const basePath = basePathProp || '';\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const tr = useTr();\n\n const [updateAgent] = useUpdateAgentMutation({ refetchQueries: ['GetAgent'] });\n // refetchQueries + awaitRefetchQueries make sure the agents list\n // (and the cached initial-data slice the AgentsPage falls back to\n // before the live query loads) drop the deleted row BEFORE we\n // navigate back, so the list doesn't briefly re-render with the\n // gone agent. cache.evict on the normalized entry covers other\n // queries that happen to reference this agent ID.\n const [deleteAgent] = useDeleteAgentMutation({\n refetchQueries: ['ListAgents', 'VibeControlsInitialData'],\n awaitRefetchQueries: true,\n update(cache, _result, { variables }) {\n const id = variables?.id;\n if (!id) return;\n cache.evict({ id: cache.identify({ __typename: 'VibecontrolsAgent', id }) });\n cache.gc();\n },\n });\n const [checkSessionHealth] = useCheckSessionHealthMutation({ refetchQueries: ['GetAgent'] });\n const [startVibecontrolsAgent] = useStartVibecontrolsAgentMutation();\n const [stopVibecontrolsAgent] = useStopVibecontrolsAgentMutation();\n // Per-action in-flight state so the Start/Stop button can show a spinner +\n // disable while the lifecycle call is running (it can take a few seconds to\n // reach the agent over its tunnel).\n const [lifecycleAction, setLifecycleAction] = useState<'start' | 'stop' | null>(null);\n\n // ── Sync active context ────────────────────────────────────────────────\n const setActiveAgentId = useVibeControlsStore((s) => s.setActiveAgentId);\n useEffect(() => {\n if (id) setActiveAgentId(id);\n }, [id, setActiveAgentId]);\n\n // ── Tab Navigation ───────────────────────────────────────────────────\n // `AgentTab` + `AGENT_TAB_IDS` are hoisted to module scope so the array\n // reference is stable across renders (memo dep correctness).\n // Active-tab declaration is deferred to after `iframeContributions`\n // (further down in this component) so the valid id set can include\n // both the static `agentTabIds` AND discovered plugin ids — see\n // the same pattern in VibeDetailsPage.\n\n // Configuration dialog open state — form state lives in ConfigureAgentDialog.\n const [showConfigModal, setShowConfigModal] = useState(false);\n\n // Inline rename state — lets operators rename the agent from the page\n // header without opening the full Configure modal. Click the pencil →\n // input swaps in. Cancel reverts to the stored name; Save fires the\n // existing UpdateAgent mutation and refetches GetAgent so every reader\n // on the page reflects the new name in the same render.\n const [renaming, setRenaming] = useState(false);\n const [renameValue, setRenameValue] = useState('');\n const [renameSaving, setRenameSaving] = useState(false);\n const [renameError, setRenameError] = useState<string | null>(null);\n const renameInputRef = useRef<HTMLInputElement>(null);\n\n const [showApiKey, setShowApiKey] = useState(false);\n const [apiKeyCopied, setApiKeyCopied] = useState(false);\n // F4 Wave B: the long-lived agent secret is no longer prefetched with\n // every GetAgent. Reveal lazily on operator click; svc-side RBAC +\n // audit gates the read.\n const [revealedApiKey, setRevealedApiKey] = useState<string | null>(null);\n const [revealLoading, setRevealLoading] = useState(false);\n const [revealError, setRevealError] = useState<string | null>(null);\n const [fetchAgentSecret] = useAgentSecretRevealLazyQuery({ fetchPolicy: 'network-only' });\n\n // Create Session dialog open state — form state lives in CreateSessionDialog\n // (ResponsiveDialog supplies Escape, focus trap and initial focus).\n const [showSessionModal, setShowSessionModal] = useState(false);\n\n // Fetch agent by ID from GraphQL API\n const { data, loading, error, refetch } = useGetAgentQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n // Get agent from response\n const agent = data?.vibecontrolsAgent;\n\n // Live deep-health probe — same source the agent cards use, so the detail\n // page's status badge matches the list. Without this the badge derived from\n // the sticky `isActive` flag, showing \"Active\" for an agent the card (and\n // reality) reports DEGRADED/stopped. Workspace-scoped query primes the cache;\n // the subscription pushes sub-second updates.\n const probeQuery = useVibecontrolsAgentHealthProbesQuery({\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n notifyOnNetworkStatusChange: true,\n skip: !id,\n pollInterval: 30_000,\n });\n const { data: probeStreamData } = useVibecontrolsAgentHealthProbeStreamSubscription({\n skip: !id,\n });\n const probesLoading = probeQuery.data === undefined && probeStreamData === undefined;\n const probesById = useMemo(() => {\n const m = new Map<string, AgentHealthProbe>();\n const probes =\n probeStreamData?.vibecontrolsAgentHealthProbeStream ??\n probeQuery.data?.vibecontrolsAgentHealthProbes ??\n [];\n for (const p of probes) {\n m.set(p.agentId, { agentId: p.agentId, healthy: p.healthy, errorMessage: p.errorMessage });\n }\n return m;\n }, [probeQuery.data, probeStreamData]);\n\n // Single source of truth for the agent's displayed status — live /health/ready\n // probe + lifecycleState, NOT the sticky isActive flag. Drives both the status\n // badge and the Start/Stop toggle so they always agree with the agent cards.\n const derivedStatus = useMemo(\n () =>\n agent\n ? deriveAgentStatus(\n {\n id: agent.id,\n isActive: agent.isActive,\n metadata: agent.metadata as Record<string, unknown> | null,\n },\n probesById,\n { probesLoading }\n )\n : 'REGISTERING',\n [agent, probesById, probesLoading]\n );\n\n // Only offer Start when the agent is known NOT to be running (STOPPED or\n // OFFLINE). For ACTIVE — and also DEGRADED / REGISTERING, where the process\n // can still be live during probe warm-up or transport errors — show Stop, so\n // an operator can always stop a live-but-unhealthy agent instead of firing a\n // start that fails as \"already running\".\n const showStartButton = derivedStatus === 'STOPPED' || derivedStatus === 'OFFLINE';\n\n // Phase 5B — resolve the active profile name for this agent. Used to\n // build canonical `/api/profiles/<name>/<rest>` paths in child tabs\n // (LogViewer, OpenInBrowserEditorButton, AgentBackupTab).\n const { active: activeProfile } = useActiveAgentProfile(agent?.id ?? null);\n\n // Plugins are agent-wide (BOFF-2612): the npm package install and the\n // \"available\" catalog are already shared across all profiles on the\n // agent; only plugin DATA (notes/sessions/etc.) stays per-profile.\n // No per-profile gate on Install/Remove anymore.\n\n // ── iframe-mount discovery (PR-8) ────────────────────────────────────\n // Gated by `vibes.tabs.iframe-host` (same flag the VibeDetailsPage\n // uses — one switch for both surfaces). When on, the host queries the\n // agent's tunnel for contributions targeting the `agent.detailTab`\n // mount point and renders them as additional tabs.\n const iframeHostFlag = useFeatureFlagDetails('vibes.tabs.iframe-host');\n const enableIframeHost = iframeHostFlag?.value === true;\n\n const iframePluginTunnels = useMemo<MountTunnel[]>(() => {\n if (!enableIframeHost || !agent?.tunnelUrl) return [];\n const agentIdForToken = agent.id;\n const profile = activeProfile ?? 'default';\n return [\n {\n tunnelUrl: agent.tunnelUrl,\n profile,\n label: agent.name ?? agent.id,\n mintIframeToken: async ({ pathPrefixes }) => {\n return mintScopedIframeToken(agentIdForToken, profile, pathPrefixes);\n },\n },\n ];\n }, [enableIframeHost, agent?.id, agent?.tunnelUrl, agent?.name, activeProfile]);\n\n const iframeContributions = usePluginContributions(iframePluginTunnels, {\n mountPoint: 'agent.detailTab',\n enabled: enableIframeHost && iframePluginTunnels.length > 0,\n });\n\n // Active-tab id set = static AGENT_TAB_IDS ∪ discovered plugin ids.\n // See VibeDetailsPage for the rationale.\n const validAgentTabIds = useMemo<readonly string[]>(() => {\n const fromContribs = iframeContributions.data\n .filter((c) => c.contribution.mountPoint === 'agent.detailTab')\n .map((c) => c.contribution.id);\n return [...(AGENT_TAB_IDS as readonly string[]), ...fromContribs];\n }, [iframeContributions.data]);\n const [activeTab, setActiveTab] = useQueryBackedTab<string>(validAgentTabIds, 'overview');\n\n const [addTabOpen, setAddTabOpen] = useState(false);\n\n // On mount, run a health check to sync stale session states with agent reality\n const healthCheckRanRef = useRef(false);\n useEffect(() => {\n if (agent && !healthCheckRanRef.current) {\n healthCheckRanRef.current = true;\n checkSessionHealth().catch(() => {\n // Best-effort — don't block the page if health check fails\n });\n }\n }, [agent, checkSessionHealth]);\n\n // Auto-recovery for agents stuck in `awaiting-config`.\n //\n // Symptom (why this exists): a freshly-booted agent loads its plugin\n // catalog but defers `onServerStart` — and therefore session-provider\n // registration — until the dashboard POSTs OAuth credentials to\n // /api/agent/gateway-auth. If the initial push failed (network blip,\n // auth-svc transient, page closed mid-flight) the agent stays stuck\n // and the badge reads \"Degraded — no session provider registered\".\n //\n // Fix: when the page loads and finds (a) an agent linked to an OAuth\n // app already, (b) reachable via tunnelUrl + agentApiKey, fire the same\n // recovery the manual \"Re-push credentials\" button uses —\n // `setupAgentGatewayAuth`. It rotates the existing OAuth app's secret on\n // auth-svc and re-pushes the fresh credentials to the agent, deriving the\n // gateway URLs/scopes server-side from the request context. The agent\n // finalizes and registers providers on receipt; self-heals the page\n // without a manual click.\n //\n // NOTE: we deliberately do NOT call `pushAgentGatewayAuth({ agentId })`\n // here — that mutation requires the full OAuth client secret in its input\n // (a server-side secret the browser does not hold), so a bare-agentId call\n // produced an INTERNAL_SERVER_ERROR. The OAuth secret is only known at\n // create/rotate time, so recovery must go through setup (rotate + push).\n const [recoverGatewayAuth] = useSetupAgentGatewayAuthMutation();\n const autoRecoveryRanRef = useRef(false);\n useEffect(() => {\n if (!agent || autoRecoveryRanRef.current) return;\n const hasApp = Boolean(agent.appId && agent.appClientId);\n const reachable = Boolean(agent.tunnelUrl);\n if (!hasApp || !reachable) return;\n autoRecoveryRanRef.current = true;\n recoverGatewayAuth({ variables: { input: { agentId: agent.id } } })\n .then(() => {\n // Re-fetch so badge + reason update once the agent finalizes.\n refetch().catch(() => {\n /* badge will refresh on next heartbeat */\n });\n })\n .catch(() => {\n // Silent — manual \"Re-push credentials\" button remains available.\n });\n }, [agent, recoverGatewayAuth, refetch]);\n\n // ── Plugin Management (via backend GraphQL proxy) ──────────────────\n interface InstalledPlugin {\n packageName: string;\n pluginName: string;\n version: string;\n installedAt: string;\n loaded: boolean;\n description?: string;\n tags?: string[];\n isCore: boolean;\n cliCommand?: string;\n apiPrefix?: string;\n hasUI: boolean;\n hasCliSetup: boolean;\n hasServerStart: boolean;\n hasServerStop: boolean;\n hasServerReady: boolean;\n hasProviders: boolean;\n }\n interface AvailablePlugin {\n packageName: string;\n name: string;\n description: string;\n version: string;\n tags: string[];\n installed: boolean;\n }\n\n const [installedPlugins, setInstalledPlugins] = useState<InstalledPlugin[]>([]);\n const [availablePlugins, setAvailablePlugins] = useState<AvailablePlugin[]>([]);\n const [pluginsLoading, setPluginsLoading] = useState(true);\n const [pluginActionLoading, setPluginActionLoading] = useState<string | null>(null);\n const [pluginError, setPluginError] = useState<string | null>(null);\n // Package currently pending confirmation/version input — drives the\n // ConfirmDialog (remove) and PromptDialog (update) that replaced the\n // old window.confirm / window.prompt calls.\n const [pluginRemoveTarget, setPluginRemoveTarget] = useState<string | null>(null);\n const [pluginUpdateTarget, setPluginUpdateTarget] = useState<string | null>(null);\n // Remove-agent dialog (declared with the other hooks, before any early\n // return, to satisfy rules-of-hooks).\n const [showRemoveDialog, setShowRemoveDialog] = useState(false);\n const [uninstallFromTarget, setUninstallFromTarget] = useState(false);\n const [deleteTargetWithAgent, setDeleteTargetWithAgent] = useState(false);\n const [removingAgent, setRemovingAgent] = useState(false);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n // Which quota the open dialog is reporting on. The plugin-install flow\n // opens the same dialog and was here first, so it's the default.\n const [quotaKind, setQuotaKind] = useState<'plugin' | 'session'>('plugin');\n // Tracks the last (agent, profile) pair we fetched plugins for, so a\n // profile switch re-fetches the profile-scoped plugin list instead of\n // showing the previous profile's plugins.\n const pluginsFetchedRef = useRef<string | null>(null);\n\n // ── Plugin Sub-Tabs & Search ──────────────────────────────────────\n type PluginSubTab = 'installed' | 'available' | 'store' | 'ui-plugins';\n const [pluginSubTab, setPluginSubTab] = useState<PluginSubTab>('installed');\n const [pluginSearchQuery, setPluginSearchQuery] = useState('');\n\n // ── Store Plugins ─────────────────────────────────────────────────\n type StoreEntitlement = {\n id: string;\n productId: string;\n licenseKey: string | null;\n product: {\n id: string;\n name: string;\n slug: string;\n icon: string | null;\n description: string | null;\n manifest: Record<string, unknown> | null;\n } | null;\n };\n const [storePlugins, setStorePlugins] = useState<StoreEntitlement[]>([]);\n const [storePluginsLoading, setStorePluginsLoading] = useState(false);\n const [storePluginsError, setStorePluginsError] = useState<string | null>(null);\n const [storeInstallingId, setStoreInstallingId] = useState<string | null>(null);\n\n // Backend-proxied plugin operations\n const [fetchAgentPlugins] = useAgentPluginsLazyQuery();\n const [installPluginMutation] = useInstallAgentPluginMutation();\n const [removePluginMutation] = useRemoveAgentPluginMutation();\n const [updatePluginMutation] = useUpdateAgentPluginMutation();\n\n const fetchPlugins = useCallback(async () => {\n if (!agent?.id) return;\n setPluginsLoading(true);\n setPluginError(null);\n try {\n const { data } = await fetchAgentPlugins({\n variables: { agentId: agent.id, profile: activeProfile },\n });\n const result = data?.agentPlugins;\n if (result) {\n const installed = (result.installed || []).map(\n (p: {\n packageName: string;\n pluginName: string;\n version: string;\n installedAt?: string | null;\n loaded: boolean;\n description?: string | null;\n tags?: (string | null)[] | null;\n isCore: boolean;\n cliCommand?: string | null;\n apiPrefix?: string | null;\n hasUI: boolean;\n hasCliSetup: boolean;\n hasServerStart: boolean;\n hasServerStop: boolean;\n hasServerReady: boolean;\n hasProviders: boolean;\n }) => ({\n packageName: p.packageName,\n pluginName: p.pluginName,\n version: p.version,\n installedAt: p.installedAt || '',\n loaded: p.loaded,\n description: p.description ?? undefined,\n tags: (p.tags?.filter(Boolean) as string[]) ?? undefined,\n isCore: p.isCore,\n cliCommand: p.cliCommand ?? undefined,\n apiPrefix: p.apiPrefix ?? undefined,\n hasUI: p.hasUI,\n hasCliSetup: p.hasCliSetup,\n hasServerStart: p.hasServerStart,\n hasServerStop: p.hasServerStop,\n hasServerReady: p.hasServerReady,\n hasProviders: p.hasProviders,\n })\n );\n const available = (result.available || []).map(\n (p: {\n packageName: string;\n name: string;\n description: string;\n version: string;\n tags: string[];\n installed: boolean;\n }) => ({\n packageName: p.packageName,\n name: p.name,\n description: p.description,\n version: p.version,\n tags: [...p.tags],\n installed: p.installed,\n })\n );\n setInstalledPlugins(installed);\n setAvailablePlugins(available);\n if (result.error) {\n setPluginError(result.error);\n }\n }\n } catch {\n // Agent unreachable — show error state\n setPluginError('Agent unreachable — unable to fetch plugin list');\n } finally {\n setPluginsLoading(false);\n }\n }, [agent?.id, fetchAgentPlugins, activeProfile]);\n\n // Attempt to fetch live plugins when agent data loads.\n // If the agent is unreachable, an error state is shown instead of fake data.\n useEffect(() => {\n if (!agent?.id) return;\n const fetchKey = `${agent.id}:${activeProfile}`;\n if (pluginsFetchedRef.current !== fetchKey) {\n pluginsFetchedRef.current = fetchKey;\n fetchPlugins();\n }\n }, [agent?.id, activeProfile, fetchPlugins]);\n\n const handleInstallPlugin = useCallback(\n async (packageName: string) => {\n if (!agent?.id) return;\n setPluginActionLoading(packageName);\n try {\n const { data } = await installPluginMutation({\n variables: { agentId: agent.id, packageName },\n });\n if (data?.installAgentPlugin?.success) {\n await fetchPlugins();\n } else {\n throw new Error(\n data?.installAgentPlugin?.error || tr('agentDetails.installFailed', 'Install failed')\n );\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setQuotaKind('plugin');\n setShowQuotaExhausted(true);\n return;\n }\n\n setPluginError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.pluginInstallFailed', 'Failed to install plugin')\n );\n setTimeout(() => setPluginError(null), 5000);\n } finally {\n setPluginActionLoading(null);\n }\n },\n [agent?.id, installPluginMutation, fetchPlugins, tr]\n );\n\n // Opens the remove-plugin ConfirmDialog (replaces window.confirm).\n const handleRemovePlugin = useCallback((packageName: string) => {\n setPluginRemoveTarget(packageName);\n }, []);\n\n const performRemovePlugin = useCallback(\n async (packageName: string) => {\n if (!agent?.id) return;\n setPluginActionLoading(packageName);\n try {\n const { data } = await removePluginMutation({\n variables: { agentId: agent.id, packageName },\n });\n if (data?.removeAgentPlugin?.success) {\n await fetchPlugins();\n } else {\n throw new Error(\n data?.removeAgentPlugin?.error || tr('agentDetails.removeFailed', 'Remove failed')\n );\n }\n } catch (err) {\n setPluginError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.pluginRemoveFailed', 'Failed to remove plugin')\n );\n setTimeout(() => setPluginError(null), 5000);\n } finally {\n setPluginActionLoading(null);\n }\n },\n [agent?.id, removePluginMutation, fetchPlugins, tr]\n );\n\n // Opens the update-plugin PromptDialog (replaces window.prompt).\n // Optional pinned version — Cancel aborts; blank = latest.\n const handleUpdatePlugin = useCallback((packageName: string) => {\n setPluginUpdateTarget(packageName);\n }, []);\n\n const performUpdatePlugin = useCallback(\n async (packageName: string, version: string | undefined) => {\n if (!agent?.id) return;\n setPluginActionLoading(packageName);\n try {\n const { data } = await updatePluginMutation({\n variables: { agentId: agent.id, packageName, version: version ?? null },\n });\n const proxy = data?.updateAgentPlugin;\n if (proxy?.ok) {\n await fetchPlugins();\n } else {\n throw new Error(proxy?.error || tr('agentDetails.updateFailed', 'Plugin update failed'));\n }\n } catch (err) {\n setPluginError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.pluginUpdateFailed', 'Failed to update plugin')\n );\n setTimeout(() => setPluginError(null), 5000);\n } finally {\n setPluginActionLoading(null);\n }\n },\n [agent?.id, updatePluginMutation, fetchPlugins, tr]\n );\n\n // Get sessions and vibes from the agent response (already included in the query)\n const agentSessions = agent?.sessions || [];\n const agentVibes = agent?.vibes || [];\n\n // ── Store plugin fetch / install ──────────────────────────────────\n const fetchStorePlugins = useCallback(async () => {\n setStorePluginsLoading(true);\n setStorePluginsError(null);\n try {\n const response = await fetch(\n (typeof window !== 'undefined' &&\n (window as { __BURDENOFF_GLOBAL_GATEWAY__?: string }).__BURDENOFF_GLOBAL_GATEWAY__) ||\n '/global/graphql',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n query: `query MyStorePlugins {\n myEntitlements {\n id\n productId\n licenseKey\n product {\n id\n name\n slug\n icon\n description\n manifest\n }\n }\n }`,\n }),\n }\n );\n const body = (await response.json()) as {\n data?: { myEntitlements?: StoreEntitlement[] };\n errors?: Array<{ message: string }>;\n };\n if (body.errors?.length)\n throw new Error(body.errors[0]?.message ?? 'Failed to load store plugins');\n const plugins = (body.data?.myEntitlements ?? []).filter((e) => {\n const manifest = e.product?.manifest as Record<string, unknown> | null;\n if (!manifest) return false;\n const itemType = (manifest['itemType'] as string | undefined)?.toLowerCase();\n return itemType === 'vibemodule' || itemType === 'plugin';\n });\n setStorePlugins(plugins);\n } catch (err) {\n setStorePluginsError(err instanceof Error ? err.message : 'Failed to load store plugins');\n } finally {\n setStorePluginsLoading(false);\n }\n }, []);\n\n useEffect(() => {\n if (pluginSubTab === 'store' && storePlugins.length === 0 && !storePluginsLoading) {\n void fetchStorePlugins();\n }\n }, [pluginSubTab, storePlugins.length, storePluginsLoading, fetchStorePlugins]);\n\n const handleInstallStorePlugin = useCallback(\n async (productId: string, licenseKey: string | null) => {\n if (!agent?.id) return;\n setStoreInstallingId(productId);\n try {\n const response = await fetch(\n (typeof window !== 'undefined' &&\n (window as { __BURDENOFF_WSPACE_GATEWAY__?: string }).__BURDENOFF_WSPACE_GATEWAY__) ||\n '/workspaces/graphql',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n query: `mutation InstallStorePlugin($agentId: ID!, $productId: ID!, $licenseKey: String) {\n installStorePluginOnAgent(agentId: $agentId, productId: $productId, licenseKey: $licenseKey) {\n success\n error\n }\n }`,\n variables: { agentId: agent.id, productId, licenseKey },\n }),\n }\n );\n const body = (await response.json()) as {\n data?: {\n installStorePluginOnAgent?: { success: boolean; error: string | null };\n };\n errors?: Array<{ message: string }>;\n };\n if (body.errors?.length) throw new Error(body.errors[0]?.message ?? 'Install failed');\n const result = body.data?.installStorePluginOnAgent;\n if (!result?.success) {\n throw new Error(result?.error ?? 'Install failed');\n }\n await fetchPlugins();\n setPluginSubTab('installed');\n } catch (err) {\n setStorePluginsError(err instanceof Error ? err.message : 'Install failed');\n } finally {\n setStoreInstallingId(null);\n }\n },\n [agent?.id, fetchPlugins]\n );\n\n // Handle ?install=<productId>&licenseKey=... on page load — automatic install flow\n // when the user arrives here from OrderConfirmationPage\n useEffect(() => {\n if (typeof window === 'undefined' || !agent?.id) return;\n const params = new URLSearchParams(window.location.search);\n const productId = params.get('install');\n const licenseKey = params.get('licenseKey');\n if (productId && productId !== storeInstallingId) {\n setPluginSubTab('store');\n void handleInstallStorePlugin(productId, licenseKey);\n // Clear the query params after initiating\n const url = new URL(window.location.href);\n url.searchParams.delete('install');\n url.searchParams.delete('licenseKey');\n window.history.replaceState({}, '', url.toString());\n }\n }, [agent?.id, handleInstallStorePlugin, storeInstallingId]);\n\n const handleRevealApiKey = useCallback(async (): Promise<string | null> => {\n if (!agent?.id) return null;\n if (revealedApiKey) return revealedApiKey;\n setRevealLoading(true);\n setRevealError(null);\n try {\n const res = await fetchAgentSecret({ variables: { agentId: agent.id } });\n const key = res.data?.vibecontrolsAgentSecretReveal ?? null;\n if (!key) {\n setRevealError('No API key configured');\n return null;\n }\n setRevealedApiKey(key);\n return key;\n } catch (err) {\n setRevealError(err instanceof Error ? err.message : 'Reveal failed');\n return null;\n } finally {\n setRevealLoading(false);\n }\n }, [agent?.id, revealedApiKey, fetchAgentSecret]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions…</p>\n </div>\n );\n if (\n permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !hasPermission(VIBE_PERMISSIONS.AGENT_READ)\n )\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleBack = () => {\n navigate('/agents');\n };\n\n // Real lifecycle control: start/stop the agent process via the backend proxy\n // (POST /agent/lifecycle/{start,stop} on the agent), not a metadata write.\n // Shows a spinner + disables the button while in flight and surfaces the\n // outcome as a toast; refreshes the agent record + live health probe so the\n // badge and the Start/Stop toggle flip immediately.\n const runLifecycle = async (action: 'start' | 'stop') => {\n if (!agent?.id || lifecycleAction) return;\n setLifecycleAction(action);\n try {\n const res =\n action === 'start'\n ? await startVibecontrolsAgent({ variables: { agentId: agent.id } })\n : await stopVibecontrolsAgent({ variables: { agentId: agent.id } });\n const payload =\n action === 'start' ? res.data?.startVibecontrolsAgent : res.data?.stopVibecontrolsAgent;\n // Only treat an explicitly-successful payload as success. A null payload\n // (network hiccup / unexpected schema shape) must not fall through to the\n // success toast.\n if (!payload || payload.success === false) {\n throw new Error(payload?.error || `Failed to ${action} agent`);\n }\n notify.success(\n action === 'start'\n ? tr('agentDetails.agentStarted', 'Agent started')\n : tr('agentDetails.agentStopped', 'Agent stopped')\n );\n // Snappy refresh — don't wait for the 30s probe poll / heartbeat sweep.\n await Promise.all([refetch(), probeQuery.refetch()]).catch(() => {});\n } catch (err) {\n const raw = err instanceof Error ? err.message : '';\n // The backend reaches the agent only over its tunnel. If the tunnel is\n // down (common after a stop, or if the host went away) the call can't\n // land — surface an actionable message instead of a raw fetch error.\n const unreachable =\n /unable to connect|fetch failed|timed? ?out|ECONN|agent returned 5|network/i.test(raw);\n notify.error(\n unreachable\n ? tr(\n 'agentDetails.agentUnreachable',\n `Couldn't reach the agent to ${action} it — it may be offline or its tunnel is down. Start it from the host with: vibe start`\n )\n : raw || tr('agentDetails.lifecycleFailed', 'Failed to {{action}} agent', { action })\n );\n } finally {\n setLifecycleAction(null);\n }\n };\n\n const handleStartAgent = () => void runLifecycle('start');\n const handleStopAgent = () => void runLifecycle('stop');\n const handleAfterDestructiveNuke = async () => {\n await Promise.all([refetch(), probeQuery.refetch()]).catch(() => {});\n navigate('/agents');\n };\n\n const handleConfigureAgent = () => {\n setShowConfigModal(true);\n };\n\n const handleRenameSave = async (): Promise<void> => {\n if (!agent?.id) return;\n const next = renameValue.trim();\n if (!next || next === agent.name) {\n setRenaming(false);\n return;\n }\n setRenameSaving(true);\n setRenameError(null);\n try {\n const result = await updateAgent({\n variables: { id: agent.id, input: { name: next } },\n });\n if (result.error) {\n throw result.error;\n }\n setRenaming(false);\n } catch (err) {\n // Most common failure mode: the (workspaceId, name) unique\n // constraint in wspace-vibecontrols-svc rejects a duplicate.\n // Surface the message inline so the operator can pick a\n // different name without losing what they typed.\n setRenameError(err instanceof Error ? err.message : String(err));\n } finally {\n setRenameSaving(false);\n }\n };\n\n const handleRenameCancel = (): void => {\n setRenaming(false);\n setRenameValue('');\n setRenameError(null);\n };\n\n const handleCopyApiKey = async () => {\n const key = await handleRevealApiKey();\n if (!key) return;\n try {\n await nativeCopyText(key);\n if (isNative()) void nativeToast('API key copied');\n setApiKeyCopied(true);\n setTimeout(() => setApiKeyCopied(false), 2000);\n } catch {\n // Fallback for non-HTTPS contexts\n console.error('Failed to copy API key');\n }\n };\n\n const handleToggleShowApiKey = async () => {\n if (!showApiKey) {\n await handleRevealApiKey();\n }\n setShowApiKey((v) => !v);\n };\n\n const handleRemoveAgent = () => {\n if (!agent?.id) return;\n setUninstallFromTarget(false);\n setDeleteTargetWithAgent(false);\n setShowRemoveDialog(true);\n };\n\n const handleConfirmRemoveAgent = async () => {\n if (!agent?.id) return;\n setRemovingAgent(true);\n try {\n await deleteAgent({\n variables: { id: agent.id, uninstallFromTarget, deleteTarget: deleteTargetWithAgent },\n });\n setShowRemoveDialog(false);\n handleBack();\n } catch (err) {\n console.error('Failed to remove agent:', err);\n } finally {\n setRemovingAgent(false);\n }\n };\n\n const handleSessionClick = (sessionId: string) => {\n navigate(`/sessions/${sessionId}`);\n };\n\n const handleStartSession = () => {\n setShowSessionModal(true);\n };\n\n const handleVibeClick = (vibeId: string) => {\n navigate(`/vibes/${vibeId}`);\n };\n\n const formatTimestamp = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('health.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('health.justNow', 'Just now');\n if (diff < 3600000) {\n return tr('agentDetails.minutesAgo', '{{count}}m ago', {\n count: Math.floor(diff / 60000),\n });\n }\n if (diff < 86400000) {\n return tr('agentDetails.hoursAgo', '{{count}}h ago', {\n count: Math.floor(diff / 3600000),\n });\n }\n return new Date(timestamp).toLocaleDateString();\n };\n\n // Loading state - wrapped in MotionPage to prevent unmount/remount animation replay\n if (loading && !agent) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n // Error state\n if (error && !agent) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </button>\n <ErrorState\n title={tr('agentDetails.failedLoad', 'Failed to load agent')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n // Not found state\n if (!agent) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </button>\n <EmptyState\n icon={<Server className=\"size-8 text-text-secondary\" />}\n title={tr('agentDetails.notFound', 'Agent not found')}\n description={`${tr('agentDetails.notFoundDescriptionPrefix', 'No agent with ID')} \"${id}\" ${tr('agentDetails.notFoundDescriptionSuffix', 'exists')}`}\n action={\n <Button type=\"button\" onClick={handleBack}>\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </Button>\n }\n />\n </MotionPage>\n );\n }\n\n const agentTabs: { id: AgentTab; label: string; icon: typeof Server; badge?: string }[] = [\n { id: 'overview', label: tr('nav.overview', 'Overview'), icon: Server },\n { id: 'profiles', label: tr('agentDetails.profiles', 'Profiles'), icon: Users },\n { id: 'security', label: tr('agentDetails.security', 'Security'), icon: Shield },\n {\n id: 'plugins',\n label: tr('agentDetails.plugins', 'Plugins'),\n icon: Puzzle,\n badge: `${installedPlugins.length}`,\n },\n {\n id: 'sessions',\n label: tr('nav.sessions', 'Sessions'),\n icon: Terminal,\n badge: `${agentSessions.length}`,\n },\n {\n id: 'logs',\n label: tr('nav.logs', 'Logs'),\n icon: ScrollText,\n },\n {\n id: 'connection',\n label: tr('agentDetails.connection', 'Connection Log'),\n icon: ScrollText,\n },\n {\n id: 'vibes',\n label: tr('nav.vibes', 'Vibes'),\n icon: Zap,\n badge: `${agentVibes.length}`,\n },\n {\n id: 'notes',\n label: tr('vibeDetails.notes', 'Notes'),\n icon: FileText,\n },\n {\n id: 'aliases',\n label: tr('agentDetails.aliases', 'Aliases'),\n icon: Command,\n },\n {\n id: 'backup',\n label: tr('agentDetails.backup', 'Backup'),\n icon: Database,\n },\n {\n id: 'maintenance',\n label: tr('agentDetails.maintenance.tab', 'Maintenance'),\n icon: Wrench,\n },\n ];\n\n // Types for sessions and vibes from the query\n type SessionItem = NonNullable<typeof agentSessions>[number];\n type VibeItem = NonNullable<typeof agentVibes>[number];\n\n // Stats\n const runningSessions = agentSessions.filter((s: SessionItem) => s.status === 'RUNNING').length;\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={agent.name}\n description={\n agent.hostname || tr('agentDetails.noHostnameConfigured', 'No hostname configured')\n }\n breadcrumb={[\n { label: tr('nav.agents', 'Agents'), path: basePath ? `${basePath}/agents` : '/agents' },\n { label: agent.name },\n ]}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n {!showStartButton ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleStopAgent}\n disabled={lifecycleAction !== null}\n aria-busy={lifecycleAction === 'stop'}\n >\n {lifecycleAction === 'stop' ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Square className=\"size-4\" aria-hidden=\"true\" />\n )}\n {lifecycleAction === 'stop'\n ? tr('agentDetails.stopping', 'Stopping…')\n : tr('sessions.stop', 'Stop')}\n </Button>\n ) : (\n <Button\n type=\"button\"\n onClick={handleStartAgent}\n disabled={lifecycleAction !== null}\n aria-busy={lifecycleAction === 'start'}\n >\n {lifecycleAction === 'start' ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Play className=\"size-4\" aria-hidden=\"true\" />\n )}\n {lifecycleAction === 'start'\n ? tr('agentDetails.starting', 'Starting…')\n : tr('agentDetails.start', 'Start')}\n </Button>\n )}\n {derivedStatus === 'DEGRADED' && (\n // BOFF-2628 — Reconnect on Degraded. Fires the same lifecycle\n // start as a fresh start; svc-side that POSTs lifecycle/start\n // to the agent's tunnel, which typically clears transient\n // failures (tunnel hiccup, finalize retry). The button is\n // labelled \"Reconnect\" so the user knows it's a recovery\n // action — not a fresh start that might collide.\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={handleStartAgent}\n disabled={lifecycleAction !== null}\n aria-busy={lifecycleAction === 'start'}\n className=\"text-status-warning-text hover:text-status-warning-text\"\n title={\n probesById.get(agent.id)?.errorMessage\n ? `Retry lifecycle/start — last error: ${probesById.get(agent.id)?.errorMessage}`\n : 'Retry lifecycle/start'\n }\n >\n {lifecycleAction === 'start' ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <RefreshCw className=\"size-4\" aria-hidden=\"true\" />\n )}\n {lifecycleAction === 'start'\n ? tr('agentDetails.reconnecting', 'Reconnecting…')\n : tr('agentDetails.reconnect', 'Reconnect')}\n </Button>\n )}\n {/* Self-contained control (probe → start → mint flow lives inside\n the component), so it stays a visible button rather than an\n ActionMenu item. */}\n <OpenInBrowserEditorButton\n agentId={agent.id}\n tunnelUrl={agent.tunnelUrl || null}\n profile={activeProfile}\n />\n <Button type=\"button\" variant=\"outline\" onClick={handleConfigureAgent}>\n <Settings className=\"size-4\" aria-hidden=\"true\" />\n {tr('agentDetails.configure', 'Configure')}\n </Button>\n <ActionMenu\n ariaLabel={tr('agentDetails.agentActions', 'Agent actions')}\n title={tr('agentDetails.agentActions', 'Agent actions')}\n align=\"end\"\n items={[\n {\n key: 'ai-chat',\n label: tr('agentDetails.aiChat', 'AI Chat'),\n icon: Bot,\n onSelect: () => navigate('/ai?contextLevel=agent'),\n },\n {\n key: 'refresh',\n label: tr('common.refresh', 'Refresh'),\n icon: RefreshCw,\n onSelect: () => void refetch(),\n disabled: loading,\n },\n // Only surface Remove to actors with agent:delete — the\n // backend @rbac(action:\"delete\") 403s otherwise, so hiding\n // the option keeps the UI honest (BOFF-2688).\n ...(hasPermission(VIBE_PERMISSIONS.AGENT_DELETE)\n ? [\n {\n key: 'remove',\n label: tr('sessions.remove', 'Remove'),\n icon: Trash2,\n onSelect: handleRemoveAgent,\n variant: 'danger' as const,\n separatorBefore: true,\n },\n ]\n : []),\n ]}\n />\n </div>\n }\n />\n\n <PagePurpose icon={<Server className=\"size-4\" />}>\n {tr(\n 'agentDetails.purpose',\n 'This page is the control panel for one agent — the program running on your machine that VibeControls uses to execute work. Here you check its health and connection, start or restart it, push gateway credentials, review its sessions and security, and copy the commands to (re)install it. Come here when an agent is degraded or you need to confirm it is reachable.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"vibecontrols-agent-details\"\n steps={[\n {\n id: 'start-session',\n label: tr('agentDetails.next.startSession', 'Start a session on this agent'),\n description: tr(\n 'agentDetails.next.startSessionDesc',\n 'Open a terminal or task that runs on this machine.'\n ),\n icon: <Plus className=\"size-4\" />,\n onClick: handleStartSession,\n },\n {\n id: 'check-connection',\n label: tr('agentDetails.next.checkConnection', 'Check the connection'),\n description: tr(\n 'agentDetails.next.checkConnectionDesc',\n 'Verify the tunnel URL and reachability on the Connection tab.'\n ),\n icon: <Server className=\"size-4\" />,\n onClick: () => setActiveTab('connection'),\n },\n {\n id: 'review-security',\n label: tr('agentDetails.next.reviewSecurity', 'Review security & credentials'),\n description: tr(\n 'agentDetails.next.reviewSecurityDesc',\n 'Confirm gateway auth is pushed and scopes are correct.'\n ),\n icon: <Bot className=\"size-4\" />,\n onClick: () => setActiveTab('security'),\n },\n ]}\n />\n\n {/* Inline rename row — quick edit for the agent's display name.\n The Configure modal still owns the full edit (tunnel URL,\n ai-context, metadata, etc.); this is the one-click affordance\n for the common case of \"I just want to rename this thing\". */}\n <div className=\"flex items-center gap-2 -mt-2\">\n {renaming ? (\n <>\n <input\n ref={renameInputRef}\n type=\"text\"\n value={renameValue}\n onChange={(e) => setRenameValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !renameSaving) {\n void handleRenameSave();\n } else if (e.key === 'Escape') {\n handleRenameCancel();\n }\n }}\n disabled={renameSaving}\n maxLength={120}\n aria-label={tr('agentDetails.renameInput', 'New agent name')}\n className=\"flex-1 max-w-md px-3 py-1.5 text-sm bg-bg-surface border border-border-subtle rounded-input text-text-primary placeholder:text-text-placeholder transition-colors focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\"\n placeholder={agent.name}\n data-testid=\"agent-details-rename-input\"\n />\n <button\n type=\"button\"\n onClick={() => void handleRenameSave()}\n disabled={!renameValue.trim() || renameValue.trim() === agent.name || renameSaving}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bgHover transition-colors disabled:opacity-60 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n data-testid=\"agent-details-rename-save\"\n >\n {renameSaving ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Check className=\"size-3.5\" />\n )}\n {tr('agentDetails.saveName', 'Save')}\n </button>\n <button\n type=\"button\"\n onClick={handleRenameCancel}\n disabled={renameSaving}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary disabled:opacity-60\"\n data-testid=\"agent-details-rename-cancel\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n {renameError && (\n <span className=\"text-xs text-status-error-text\" role=\"alert\">\n {renameError}\n </span>\n )}\n </>\n ) : (\n <button\n type=\"button\"\n onClick={() => {\n setRenameError(null);\n setRenameValue(agent.name);\n setRenaming(true);\n requestAnimationFrame(() => renameInputRef.current?.select());\n }}\n className=\"inline-flex items-center gap-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n title={tr('agentDetails.renameTooltip', 'Rename this agent')}\n data-testid=\"agent-details-rename-trigger\"\n >\n <Edit3 className=\"size-3.5\" />\n {tr('agentDetails.rename', 'Rename')}\n </button>\n )}\n </div>\n\n {/* Auto-reconnecting banner — shown only while the badge says DEGRADED.\n Pairs with the visible Reconnect button above so the user can\n either wait for the next health probe (every ~30s) to recover the\n agent automatically, or click Reconnect to retry now. The latest\n probe error message and the suggested action items are surfaced\n inline instead of hiding them behind a tooltip on the badge. */}\n {derivedStatus === 'DEGRADED' && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex items-start gap-3 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle px-4 py-3 text-sm text-text-primary\"\n >\n <Loader2\n className=\"size-4 mt-0.5 flex-shrink-0 animate-spin text-status-warning-text\"\n aria-hidden=\"true\"\n />\n <div className=\"flex-1 min-w-0 space-y-1\">\n <p className=\"font-medium\">\n {tr(\n 'agentDetails.degradedBanner.title',\n 'Agent is degraded — auto-reconnecting in the background'\n )}\n </p>\n {probesById.get(agent.id)?.errorMessage && (\n <p className=\"text-xs text-text-secondary\">\n <span className=\"font-mono text-status-warning-text\">\n {probesById.get(agent.id)?.errorMessage}\n </span>\n </p>\n )}\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'agentDetails.degradedBanner.action',\n 'Health probes run every ~30s; the badge flips back to Active automatically once a probe succeeds. You can click Reconnect above to retry now, or check the Connection tab for the probe history.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => setActiveTab('connection')}\n className=\"text-xs font-medium text-text-link hover:underline flex-shrink-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus rounded\"\n >\n {tr('agentDetails.degradedBanner.viewLog', 'View log →')}\n </button>\n </div>\n )}\n\n {/* Status Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Server className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">{tr('sessions.filterStatus', 'Status')}</p>\n <AgentStatusBadge\n status={derivedStatus}\n reason={\n probesById.get(agent.id)?.errorMessage ??\n (agent.lastGatewayAuthPushStatus === 'failed'\n ? (agent.lastGatewayAuthPushError ?? 'Gateway-auth push failed')\n : null)\n }\n linkTo={`?tab=connection`}\n onClick={(event) => {\n event.preventDefault();\n setActiveTab('connection');\n }}\n />\n </div>\n </div>\n </Card>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Cpu className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">{tr('analytics.platform', 'Platform')}</p>\n <p className=\"font-semibold text-text-primary\">\n {agent.platform}/{agent.architecture}\n </p>\n </div>\n </div>\n </Card>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <HardDrive className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">{tr('agentDetails.version', 'Version')}</p>\n <p className=\"font-semibold text-text-primary\">v{agent.version}</p>\n </div>\n </div>\n </Card>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Terminal className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">{tr('nav.sessions', 'Sessions')}</p>\n <p className=\"font-semibold text-text-primary\">\n {runningSessions} {tr('health.active', 'active')}\n </p>\n </div>\n </div>\n </Card>\n </div>\n\n {/* ── Tab Navigation Bar ─────────────────────────────────────────── */}\n {/* fe-libs chrome Tabs supplies tablist/tab roles + arrow-key nav.\n Static tabs and PR-8 iframe-mounted plugin tabs (feature-flagged,\n same gate as VibeDetailsPage) merge into one strip; \"+ Add tab\"\n stays a separate trailing button since it opens a dialog rather\n than activating a panel. */}\n <div className=\"flex items-end border-b border-border-default\">\n <Tabs\n items={[\n ...agentTabs.map(\n ({ id: tabId, label, icon: TabIcon, badge }): TabItem => ({\n id: tabId,\n label,\n icon: <TabIcon className=\"size-4\" aria-hidden=\"true\" />,\n badge,\n })\n ),\n ...(enableIframeHost\n ? iframeContributions.data.map(\n (c): TabItem => ({\n id: c.contribution.id,\n label: c.contribution.title,\n })\n )\n : []),\n ]}\n activeTabId={activeTab}\n onTabChange={setActiveTab}\n variant=\"underline\"\n className=\"min-w-0 flex-1 border-b-0\"\n />\n {/* \"+ Add tab\" — placed at the trailing end of the strip. */}\n {enableIframeHost && agent && (\n <button\n type=\"button\"\n onClick={() => setAddTabOpen(true)}\n className=\"-mb-px flex shrink-0 items-center gap-1 p-2 text-xs sm:text-sm border-b-2 border-transparent text-text-secondary hover:text-text-primary hover:border-border-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus rounded-t\"\n title=\"Install a UI plugin tab\"\n aria-label=\"Add tab\"\n >\n <span aria-hidden>+</span>\n <span className=\"hidden sm:inline\">Add tab</span>\n </button>\n )}\n </div>\n\n {/* ── Tab Panels ─────────────────────────────────────────────────── */}\n <MotionTabPanel tabKey={activeTab}>\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"space-y-6\">\n {/* Capabilities */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <CheckCircle2 className=\"size-5\" />\n {tr('agentDetails.capabilities', 'Capabilities')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"flex flex-wrap gap-2\">\n {agent.capabilities &&\n typeof agent.capabilities === 'object' &&\n (Array.isArray(agent.capabilities)\n ? agent.capabilities.length > 0\n : Object.keys(agent.capabilities).length > 0) ? (\n (Array.isArray(agent.capabilities)\n ? (agent.capabilities as string[])\n : (() => {\n const obj = agent.capabilities as Record<string, unknown>;\n const keys = Object.keys(obj);\n const hasNumericKeys =\n keys.length > 0 && keys.every((k) => /^\\d+$/.test(k));\n return hasNumericKeys\n ? (Object.values(obj) as string[])\n : (keys as string[]);\n })()\n ).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 bg-bg-sunken text-text-secondary text-xs rounded-pill\"\n >\n {cap}\n </span>\n ))\n ) : (\n <div className=\"space-y-2 text-sm text-text-secondary\">\n <p>\n {tr('agentDetails.noCapabilitiesConfigured', 'No capabilities configured')}\n </p>\n <p className=\"text-xs\">\n Capabilities are feature tags the agent advertises to vibes (e.g.{' '}\n <code className=\"text-text-primary\">docker</code>,{' '}\n <code className=\"text-text-primary\">gpu</code>,{' '}\n <code className=\"text-text-primary\">node20</code>,{' '}\n <code className=\"text-text-primary\">git</code>). Vibes use them to pick an\n agent that can run their workloads.\n </p>\n <p className=\"text-xs\">\n Add from the agent host CLI:{' '}\n <code className=\"text-text-primary\">vibe agent caps add docker gpu</code>.\n The agent re-publishes its capability set on next heartbeat.\n </p>\n </div>\n )}\n </div>\n </div>\n </Card>\n\n {/* Tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Tag className=\"size-5\" />\n Tags\n </h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_AGENT\" entityId={agent.id} />\n </div>\n </Card>\n\n {/* Tunnel Info */}\n {agent.tunnelUrl && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Zap className=\"size-5\" />\n {tr('health.tunnel', 'Tunnel')}\n </h3>\n </div>\n <div className=\"p-4\">\n {/*\n * Tunnel URL is a plain info field — no status chip\n * here. The agent's canonical status lives in the\n * top \"Status\" card above; rendering tunnelStatus\n * here was a redundant (and frequently stale)\n * second status surface.\n */}\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm text-text-secondary mb-1\">\n {tr('agentDetails.tunnelUrl', 'Tunnel URL')}\n </p>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-text-link hover:underline break-all\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n </div>\n </Card>\n )}\n\n {/* Configuration */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Settings className=\"size-5\" />\n {tr('agentDetails.configuration', 'Configuration')}\n </h3>\n </div>\n <div className=\"p-4 space-y-4\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('analytics.platform', 'Platform')}\n </p>\n <p className=\"font-medium text-text-primary\">{agent.platform}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('analytics.architecture', 'Architecture')}\n </p>\n <p className=\"font-medium text-text-primary\">{agent.architecture}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('agentDetails.version', 'Version')}\n </p>\n <p className=\"font-medium text-text-primary\">{agent.version}</p>\n </div>\n </div>\n {agent.aiContext && (\n <div>\n <p className=\"text-sm text-text-secondary mb-1\">AI Context</p>\n <pre className=\"text-sm text-text-primary bg-bg-sunken border border-border-default rounded-md p-3 whitespace-pre-wrap font-sans\">\n {agent.aiContext}\n </pre>\n </div>\n )}\n </div>\n </Card>\n\n {/* Timeline */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Clock className=\"size-5\" />\n {tr('vibeDetails.timeline', 'Timeline')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">\n {tr('health.lastHeartbeat', 'Last Heartbeat')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(agent.lastHeartbeat)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('vibeDetails.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(agent.createdAt)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('vibeDetails.updated', 'Updated')}</p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(agent.updatedAt)}\n </p>\n </div>\n </div>\n </div>\n </Card>\n </div>\n )}\n\n {/* Profiles Tab — Phase 5B (multi-profile-per-daemon) */}\n {activeTab === 'profiles' && <ProfilesPanel agentId={agent.id} />}\n\n {/* Security Tab */}\n {activeTab === 'security' && (\n <div className=\"space-y-6\">\n {/* Agent API Key */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Key className=\"size-5\" />\n {tr('agentDetails.agentApiKey', 'Agent API Key')}\n </h3>\n </div>\n <div className=\"p-4\">\n {agent.tunnelUrl ? (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex-1 min-w-0\">\n <code className=\"text-sm font-mono text-text-primary bg-bg-sunken px-3 py-2 rounded-md block truncate\">\n {showApiKey && revealedApiKey\n ? revealedApiKey\n : revealLoading\n ? tr('agentDetails.revealLoading', 'Revealing\\u2026')\n : '\\u2022'.repeat(32)}\n </code>\n {revealError && (\n <p className=\"text-xs text-status-error-text mt-1\">{revealError}</p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => void handleToggleShowApiKey()}\n disabled={revealLoading}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors flex-shrink-0 disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n aria-label={\n showApiKey\n ? tr('agentDetails.hideApiKey', 'Hide API key')\n : tr('agentDetails.showApiKey', 'Show API key')\n }\n title={\n showApiKey\n ? tr('agentDetails.hideApiKey', 'Hide API key')\n : tr('agentDetails.showApiKey', 'Show API key')\n }\n >\n {showApiKey ? (\n <EyeOff className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n ) : (\n <Eye className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n )}\n </button>\n <button\n type=\"button\"\n onClick={handleCopyApiKey}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors flex-shrink-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n aria-label={tr('agentDetails.copyApiKey', 'Copy API key')}\n title={tr('agentDetails.copyApiKey', 'Copy API key')}\n >\n {apiKeyCopied ? (\n <Check className=\"size-4 text-status-success-text\" aria-hidden=\"true\" />\n ) : (\n <Copy className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n )}\n </button>\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Agent tunnel not configured. Bring the agent online first.\n </p>\n )}\n <p className=\"mt-2 text-xs text-text-muted\">\n This key authenticates requests sent to the agent. The key is revealed only on\n explicit operator action and every read is audited.\n </p>\n </div>\n </Card>\n\n {/* Emergency connection recovery — set API key (from `vibe key`)\n or update the tunnel URL in place when auto-report is broken,\n without delete + re-add. */}\n <AgentConnectionRecovery\n agentId={agent.id}\n currentTunnelUrl={agent.tunnelUrl}\n onUpdated={() => refetch()}\n />\n\n {/* Reuse an existing database for this agent. */}\n <AgentStorageConfig\n agentId={agent.id}\n storageAdapter={agent.storageAdapter}\n dbPath={agent.dbPath}\n hasStorageOptions={agent.hasStorageOptions}\n onUpdated={() => refetch()}\n />\n\n {/* Gateway Auth (OAuth2 App) */}\n <GatewayAuthCard agent={agent} onUpdate={() => refetch()} />\n\n {/* Remote Task Permissions — opt-in toggles for the\n \"command/script over tunnel\" + \"file write/delete over tunnel\"\n gates that block VibeDeck buttons / scheduled jobs by default. */}\n <RemoteTaskPermissionsCard\n agent={{ id: agent.id, profile: activeProfile ?? 'default' }}\n />\n </div>\n )}\n\n {/* Plugins Tab */}\n {activeTab === 'plugins' &&\n (() => {\n const searchLower = pluginSearchQuery.toLowerCase();\n const filteredInstalled = installedPlugins.filter(\n (p) =>\n !searchLower ||\n p.pluginName.toLowerCase().includes(searchLower) ||\n p.packageName.toLowerCase().includes(searchLower) ||\n p.description?.toLowerCase().includes(searchLower) ||\n p.tags?.some((t) => t.toLowerCase().includes(searchLower))\n );\n const filteredAvailable = availablePlugins.filter(\n (p) =>\n !p.installed &&\n (!searchLower ||\n p.name.toLowerCase().includes(searchLower) ||\n p.packageName.toLowerCase().includes(searchLower) ||\n p.description.toLowerCase().includes(searchLower))\n );\n const uiPlugins = installedPlugins.filter((p) => p.hasUI);\n const filteredUIPlugins = uiPlugins.filter(\n (p) =>\n !searchLower ||\n p.pluginName.toLowerCase().includes(searchLower) ||\n p.packageName.toLowerCase().includes(searchLower)\n );\n\n const pluginSubTabs: {\n id: PluginSubTab;\n label: string;\n count: number;\n icon: typeof Puzzle;\n }[] = [\n {\n id: 'installed',\n label: 'Installed',\n count: filteredInstalled.length,\n icon: Puzzle,\n },\n {\n id: 'available',\n label: 'Available',\n count: filteredAvailable.length,\n icon: Download,\n },\n {\n id: 'store',\n label: 'Store',\n count: storePlugins.length,\n icon: Download,\n },\n {\n id: 'ui-plugins',\n label: 'UI Plugins',\n count: filteredUIPlugins.length,\n icon: Monitor,\n },\n ];\n\n return (\n <div className=\"space-y-6\">\n <Card>\n {/* Header with title, search, and refresh */}\n <div className=\"p-4 border-b border-border-default space-y-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2 min-w-0\">\n <Puzzle className=\"size-5\" aria-hidden=\"true\" />\n Plugins\n {installedPlugins.length > 0 && (\n <span className=\"text-sm font-normal text-text-secondary\">\n ({installedPlugins.filter((p) => p.isCore).length} core,{' '}\n {installedPlugins.filter((p) => !p.isCore).length} external)\n </span>\n )}\n </h3>\n <button\n type=\"button\"\n onClick={fetchPlugins}\n disabled={pluginsLoading}\n className=\"p-1.5 text-text-secondary hover:bg-bg-sunken rounded transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n aria-label={tr('agentDetails.refreshPlugins', 'Refresh plugins')}\n title={tr('agentDetails.refreshPlugins', 'Refresh plugins')}\n >\n <RefreshCw\n className={`size-4 ${pluginsLoading ? 'animate-spin' : ''}`}\n aria-hidden=\"true\"\n />\n </button>\n </div>\n\n {/* Search bar */}\n <SearchInput\n value={pluginSearchQuery}\n onChange={setPluginSearchQuery}\n placeholder={tr(\n 'agentDetails.searchPluginsPlaceholder',\n 'Search plugins by name, package, description, or tag...'\n )}\n />\n\n {/* Sub-tabs */}\n <Tabs\n items={pluginSubTabs.map(\n ({ id: subTabId, label, count, icon: SubTabIcon }): TabItem => ({\n id: subTabId,\n label,\n icon: <SubTabIcon className=\"size-4\" aria-hidden=\"true\" />,\n badge: count,\n })\n )}\n activeTabId={pluginSubTab}\n onTabChange={(subTabId) => setPluginSubTab(subTabId as PluginSubTab)}\n variant=\"pills\"\n size=\"sm\"\n className=\"max-w-full overflow-x-auto\"\n />\n </div>\n\n <div className=\"p-4 space-y-4\">\n {/* Plugin Error */}\n {pluginError && (\n <div\n role=\"alert\"\n className=\"flex items-center gap-2 p-3 bg-status-error-bg-subtle border border-status-error-border text-status-error-text rounded-md text-sm\"\n >\n <X className=\"size-4 flex-shrink-0\" aria-hidden=\"true\" />\n <span className=\"flex-1 min-w-0 break-words\">{pluginError}</span>\n <button\n type=\"button\"\n onClick={() => setPluginError(null)}\n aria-label={tr('agentDetails.dismissError', 'Dismiss error')}\n className=\"flex-shrink-0 p-0.5 hover:bg-status-error-border/40 rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n </div>\n )}\n\n {/* Not reachable banner */}\n {!agent?.isActive && (\n <p\n role=\"status\"\n className=\"text-sm text-text-muted flex items-center gap-1.5\"\n >\n <Info className=\"size-4 flex-shrink-0\" aria-hidden=\"true\" />\n Agent is offline — showing known plugin catalog. Live data will load when\n the agent is reachable.\n </p>\n )}\n\n {/* Plugins are agent-wide (BOFF-2612). No per-profile\n gating: a single install/remove action applies to the\n whole agent. Only plugin DATA stays per-profile. */}\n\n {/* ── Installed Sub-Tab ────────────────────────────────── */}\n {pluginSubTab === 'installed' && (\n <>\n {filteredInstalled.length > 0 ? (\n <div className=\"space-y-2\">\n {filteredInstalled.map((plugin) => (\n <div\n key={plugin.packageName}\n className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2 p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary flex items-center gap-1.5 flex-wrap\">\n {plugin.pluginName}\n <span className=\"text-xs text-text-muted font-normal\">\n {plugin.version === 'latest'\n ? plugin.version\n : `v${plugin.version}`}\n </span>\n {plugin.isCore && (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n core\n </span>\n )}\n {plugin.loaded && (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs bg-status-success-bg-subtle text-status-success-text rounded\">\n loaded\n </span>\n )}\n {plugin.hasProviders && (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n provider\n </span>\n )}\n {plugin.hasUI && (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n ui\n </span>\n )}\n </p>\n {plugin.description && (\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {plugin.description}\n </p>\n )}\n <div className=\"flex items-center gap-2 mt-0.5 flex-wrap\">\n {!plugin.isCore && (\n <span className=\"text-xs text-text-muted font-mono\">\n {plugin.packageName}\n </span>\n )}\n {plugin.cliCommand && (\n <span className=\"text-xs text-text-muted\">\n cli:{' '}\n <code className=\"text-xs font-mono\">\n vibe {plugin.cliCommand}\n </code>\n </span>\n )}\n {plugin.apiPrefix && (\n <span className=\"text-xs text-text-muted\">\n api:{' '}\n <code className=\"text-xs font-mono\">\n {plugin.apiPrefix}\n </code>\n </span>\n )}\n </div>\n {plugin.tags && plugin.tags.length > 0 && (\n <div className=\"flex gap-1 mt-1 flex-wrap\">\n {plugin.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex px-1.5 py-0.5 text-xs bg-bg-sunken text-text-muted rounded\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2 flex-shrink-0 sm:ml-3\">\n {plugin.hasUI && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPluginSubTab('ui-plugins')}\n title={`Open ${plugin.pluginName} UI`}\n >\n <Monitor className=\"size-3\" aria-hidden=\"true\" />\n Open UI\n </Button>\n )}\n {!plugin.isCore && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleUpdatePlugin(plugin.packageName)}\n disabled={pluginActionLoading === plugin.packageName}\n title={tr(\n 'agentDetails.updatePlugin',\n 'Update plugin (latest or a specific version)'\n )}\n >\n {pluginActionLoading === plugin.packageName ? (\n <Loader2\n className=\"size-3 animate-spin\"\n aria-hidden=\"true\"\n />\n ) : (\n <ArrowUpCircle className=\"size-3\" aria-hidden=\"true\" />\n )}\n {tr('agentDetails.update', 'Update')}\n </Button>\n )}\n {!plugin.isCore && (\n // removeAgentPlugin is @rbac(action:\"update\",\n // resource:\"agent\") — hide the destructive\n // Remove for actors without agent:update so\n // the backend 403 isn't surfaced (BOFF-2688).\n <PermissionButton\n permission={VIBE_PERMISSIONS.AGENT_UPDATE}\n deniedBehavior=\"hide\"\n >\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleRemovePlugin(plugin.packageName)}\n disabled={pluginActionLoading === plugin.packageName}\n className=\"text-status-error-text hover:bg-status-error-bg-subtle hover:text-status-error-text\"\n >\n {pluginActionLoading === plugin.packageName ? (\n <Loader2\n className=\"size-3 animate-spin\"\n aria-hidden=\"true\"\n />\n ) : (\n <Trash2 className=\"size-3\" aria-hidden=\"true\" />\n )}\n {tr('sessions.remove', 'Remove')}\n </Button>\n </PermissionButton>\n )}\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-muted\">\n {pluginSearchQuery\n ? 'No installed plugins match your search.'\n : 'No plugin data available. Check agent connectivity or refresh.'}\n </p>\n )}\n </>\n )}\n\n {/* ── Available Sub-Tab ────────────────────────────────── */}\n {pluginSubTab === 'available' && (\n <>\n {filteredAvailable.length > 0 ? (\n <div className=\"space-y-2\">\n {filteredAvailable.map((plugin) => (\n <div\n key={plugin.packageName}\n className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2 p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {plugin.name}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {plugin.description}\n </p>\n <p className=\"text-xs text-text-muted font-mono mt-0.5\">\n {plugin.packageName}\n </p>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => handleInstallPlugin(plugin.packageName)}\n disabled={pluginActionLoading === plugin.packageName}\n className=\"flex-shrink-0 sm:ml-3\"\n >\n {pluginActionLoading === plugin.packageName ? (\n <Loader2 className=\"size-3 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Download className=\"size-3\" aria-hidden=\"true\" />\n )}\n Install\n </Button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-muted\">\n {pluginSearchQuery\n ? 'No available plugins match your search.'\n : availablePlugins.every((p) => p.installed)\n ? 'All available plugins are already installed.'\n : 'No available plugins found.'}\n </p>\n )}\n </>\n )}\n\n {/* ── Store Sub-Tab (Burdenoff Store purchased plugins) ── */}\n {pluginSubTab === 'store' && (\n <>\n {storePluginsError && (\n <div\n role=\"alert\"\n className=\"flex items-center gap-2 p-3 bg-status-error-bg-subtle border border-status-error-border text-status-error-text rounded-md text-sm\"\n >\n <X className=\"size-4 flex-shrink-0\" aria-hidden=\"true\" />\n <span className=\"flex-1 min-w-0 break-words\">{storePluginsError}</span>\n </div>\n )}\n {storePluginsLoading ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Loading purchased plugins…\n </p>\n ) : storePlugins.length === 0 ? (\n <div className=\"py-8 text-center\">\n <p className=\"text-sm text-text-muted\">\n No plugins purchased from the Burdenoff store yet.\n </p>\n <a\n href=\"/store/marketplace\"\n className=\"mt-2 inline-flex items-center gap-1 text-xs font-semibold text-text-link hover:underline\"\n >\n Browse the store →\n </a>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {storePlugins.map((ent) => {\n const manifest =\n (ent.product?.manifest as Record<string, unknown> | null) ?? {};\n const digital = manifest['digital'] as\n | Record<string, unknown>\n | undefined;\n const install = digital?.['install'] as\n | Record<string, unknown>\n | undefined;\n const packageName =\n typeof install?.['packageName'] === 'string'\n ? (install['packageName'] as string)\n : null;\n const isAlreadyInstalled = installedPlugins.some(\n (p) => p.packageName === packageName\n );\n return (\n <div\n key={ent.id}\n className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2 p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n {ent.product?.icon ? (\n <img\n src={ent.product.icon}\n alt=\"\"\n className=\"size-10 rounded-md object-cover flex-shrink-0\"\n />\n ) : (\n <div className=\"size-10 flex-shrink-0 rounded-md bg-bg-sunken flex items-center justify-center\">\n <Puzzle\n className=\"size-5 text-text-muted\"\n aria-hidden=\"true\"\n />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h4 className=\"text-sm font-semibold text-text-primary truncate\">\n {ent.product?.name ?? 'Unknown'}\n </h4>\n {ent.licenseKey && (\n <span className=\"text-xs bg-status-success-bg-subtle text-status-success-text px-1.5 py-0.5 rounded-full\">\n Licensed\n </span>\n )}\n </div>\n {packageName && (\n <p className=\"text-xs text-text-muted font-mono truncate\">\n {packageName}\n </p>\n )}\n {ent.product?.description && (\n <p className=\"text-xs text-text-secondary line-clamp-2 mt-1\">\n {ent.product.description}\n </p>\n )}\n </div>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() =>\n void handleInstallStorePlugin(ent.productId, ent.licenseKey)\n }\n disabled={\n isAlreadyInstalled ||\n storeInstallingId === ent.productId ||\n !packageName\n }\n aria-busy={storeInstallingId === ent.productId}\n className=\"flex-shrink-0\"\n >\n {isAlreadyInstalled\n ? 'Installed'\n : storeInstallingId === ent.productId\n ? 'Installing…'\n : 'Install'}\n </Button>\n </div>\n );\n })}\n </div>\n )}\n </>\n )}\n\n {/* ── UI Plugins Sub-Tab (Embedded Browser) ────────────── */}\n {pluginSubTab === 'ui-plugins' && (\n <>\n {uiPlugins.length > 0 || agent?.tunnelUrl ? (\n <PluginUIPanel\n availablePlugins={[\n // Synthetic code-server entry (proxied, not a standard UI plugin)\n ...(agent?.tunnelUrl\n ? [\n {\n pluginName: 'code-server',\n displayName: 'Code Server (VS Code)',\n description: 'Browser-based VS Code editor',\n version: 'latest',\n customUrl: '/code-server/',\n } satisfies UIPluginOption,\n ]\n : []),\n ...uiPlugins.map(\n (p): UIPluginOption => ({\n pluginName: p.pluginName,\n displayName: p.pluginName\n .replace(/^ui-/, '')\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, (c) => c.toUpperCase()),\n description: p.description,\n version: p.version,\n })\n ),\n ]}\n tunnelUrl={agent?.tunnelUrl || null}\n agentId={agent?.id ?? null}\n profile={activeProfile}\n />\n ) : (\n <p className=\"text-sm text-text-muted\">\n {pluginSearchQuery\n ? 'No UI plugins match your search.'\n : 'No plugins with UI are currently installed.'}\n </p>\n )}\n </>\n )}\n\n {/* Loading */}\n {pluginsLoading && installedPlugins.length === 0 && (\n <div\n role=\"status\"\n className=\"flex items-center gap-2 text-sm text-text-secondary\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n Loading plugins...\n </div>\n )}\n </div>\n </Card>\n </div>\n );\n })()}\n\n {/* Sessions Tab */}\n {activeTab === 'sessions' && (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between gap-3 flex-wrap\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"size-5\" />\n {tr('nav.sessions', 'Sessions')} ({agentSessions.length})\n </h3>\n <PermissionButton\n permission={VIBE_PERMISSIONS.SESSION_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip=\"You don't have permission to create sessions\"\n >\n <Button\n type=\"button\"\n size=\"sm\"\n data-testid=\"create-session-btn\"\n onClick={handleStartSession}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('sessions.createSession', 'Create Session')}\n </Button>\n </PermissionButton>\n </div>\n {agentSessions.length > 0 ? (\n <div className=\"divide-y divide-border-default\">\n {agentSessions.map((session: SessionItem) => (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleSessionClick(session.id);\n }\n }}\n key={session.id}\n onClick={() => handleSessionClick(session.id)}\n className=\"p-4 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 cursor-pointer hover:bg-bg-sunken/50 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-inset\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <Terminal\n className=\"size-5 text-text-secondary flex-shrink-0\"\n aria-hidden=\"true\"\n />\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{session.name}</p>\n <p className=\"text-sm text-text-secondary truncate\">{session.type}</p>\n </div>\n </div>\n <StatusBadge status={session.status} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"p-6 text-center text-text-secondary flex flex-col items-center gap-3\">\n <span>{tr('agentDetails.noSessionsOnAgent', 'No sessions on this agent')}</span>\n <PermissionButton\n permission={VIBE_PERMISSIONS.SESSION_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip=\"You don't have permission to create sessions\"\n >\n <Button\n type=\"button\"\n size=\"sm\"\n data-testid=\"create-session-empty-btn\"\n onClick={handleStartSession}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('sessions.createSession', 'Create Session')}\n </Button>\n </PermissionButton>\n </div>\n )}\n </Card>\n )}\n\n {activeTab === 'connection' && (\n <Card className=\"min-h-[60vh]\">\n <ConnectionLogPanel agentId={agent.id} agentName={agent.name} />\n </Card>\n )}\n\n {activeTab === 'logs' && (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <ScrollText className=\"size-5\" />\n {tr('nav.logs', 'Logs')}\n </h3>\n </div>\n <div className=\"p-4\">\n <LogViewer agentId={agent.id} profile={activeProfile} className=\"min-h-[28rem]\" />\n </div>\n </Card>\n )}\n\n {/* Vibes Tab */}\n {activeTab === 'vibes' && (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Zap className=\"size-5\" />\n {tr('nav.vibes', 'Vibes')} ({agentVibes.length})\n </h3>\n </div>\n {agentVibes.length > 0 ? (\n <div className=\"divide-y divide-border-default\">\n {agentVibes.map((vibe: VibeItem) => (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleVibeClick(vibe.id);\n }\n }}\n key={vibe.id}\n onClick={() => handleVibeClick(vibe.id)}\n className=\"p-4 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 cursor-pointer hover:bg-bg-sunken/50 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-inset\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <Zap\n className=\"size-5 text-text-secondary flex-shrink-0\"\n aria-hidden=\"true\"\n />\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{vibe.name}</p>\n <p className=\"text-sm text-text-secondary truncate\">{vibe.type}</p>\n </div>\n </div>\n <StatusBadge status={vibe.status} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"p-6 text-center text-text-secondary\">\n {tr('agentDetails.noVibesUsingAgent', 'No vibes using this agent')}\n </div>\n )}\n </Card>\n )}\n\n {/* Notes Tab */}\n {activeTab === 'notes' && (\n <div className=\"space-y-6\">\n <NotesTabPanel\n context=\"agent\"\n agentVibes={agentVibes.map((v: VibeItem) => ({ id: v.id, name: v.name }))}\n />\n </div>\n )}\n\n {/* Aliases Tab */}\n {activeTab === 'aliases' && (\n <div className=\"space-y-6\">\n <AliasManager\n agent={{\n id: agent.id,\n profile:\n ((agent.metadata as Record<string, unknown> | null)?.profile as\n | string\n | undefined) ?? 'default',\n }}\n />\n </div>\n )}\n\n {activeTab === 'backup' && agent.tunnelUrl && (\n <AgentBackupTab agentId={agent.id} profile={activeProfile} />\n )}\n\n {activeTab === 'backup' && !agent.tunnelUrl && (\n <Card className=\"p-6 text-center text-text-secondary\">\n <Database className=\"size-8 mx-auto mb-2 text-text-muted\" aria-hidden=\"true\" />\n <p>\n {tr(\n 'agentDetails.backupRequiresTunnelAndApiKey',\n 'Agent tunnel URL and API key are required for backup management.'\n )}\n </p>\n <p className=\"text-sm mt-1\">\n {tr(\n 'agentDetails.configureConnectionInOverview',\n 'Configure the agent connection in the Overview tab.'\n )}\n </p>\n </Card>\n )}\n\n {activeTab === 'maintenance' && (\n <AgentMaintenanceTab\n agentId={agent.id}\n agentVersion={agent.version}\n onAfterDestructiveNuke={handleAfterDestructiveNuke}\n />\n )}\n {/* PR-8 iframe-mounted tab body. Mirrors the VibeDetailsPage\n implementation; both pages currently use the same helper\n component (inline in VibeDetailsPage), but for now we\n duplicate the mount logic here for clarity. PR-11+ unifies\n via `useMountTabStrip`. */}\n {enableIframeHost &&\n (() => {\n const active: DiscoveredContribution | undefined = iframeContributions.data.find(\n (c) => c.contribution.id === activeTab\n );\n if (!active || !agent) return null;\n return (\n <div className=\"space-y-6\">\n <Card>\n <div className=\"p-0 h-[70vh] max-h-[40rem] min-h-[20rem]\">\n <PluginIframeMount\n contribution={active}\n tunnel={active.tunnel}\n restPaths={\n active.capabilities.restPaths.length > 0\n ? active.capabilities.restPaths\n : [`/ui/${active.pluginKey}`]\n }\n context={{ agentId: agent.id }}\n agentId={agent.id}\n profile={active.tunnel.profile}\n theme={{ colorMode: 'light', tokens: {} }}\n locale=\"en\"\n featureFlags={{}}\n hostVersion=\"1.0.0\"\n />\n </div>\n </Card>\n </div>\n );\n })()}\n </MotionTabPanel>\n\n {/* PR-10 \"+ Add tab\" picker. */}\n {enableIframeHost && agent && (\n <AddTabDialog\n isOpen={addTabOpen}\n onClose={() => setAddTabOpen(false)}\n mountPoint=\"agent.detailTab\"\n agentId={agent.id}\n profile={activeProfile ?? 'default'}\n onInstalled={() => {\n void iframeContributions.refetch();\n }}\n />\n )}\n\n {/* Configure Agent Dialog (extracted — see components/agents) */}\n <ConfigureAgentDialog\n agent={{\n id: agent.id,\n name: agent.name,\n platform: agent.platform,\n architecture: agent.architecture,\n version: agent.version,\n tunnelUrl: agent.tunnelUrl,\n aiContext: agent.aiContext,\n metadata: agent.metadata as Record<string, unknown> | null,\n }}\n isOpen={showConfigModal}\n onClose={() => setShowConfigModal(false)}\n onSaved={() =>\n notify.success(\n tr('agentDetails.configurationUpdated', 'Agent configuration updated successfully')\n )\n }\n />\n\n {/* Create Session Dialog (extracted — see components/agents) */}\n <CreateSessionDialog\n agentId={agent.id}\n agentName={agent.name}\n probe={probesById.get(agent.id)}\n isOpen={showSessionModal}\n onClose={() => setShowSessionModal(false)}\n onCreated={() =>\n notify.success(tr('sessions.createdSuccess', 'Session created successfully'))\n }\n onQuotaExhausted={() => {\n setShowSessionModal(false);\n markQuotaHandledLocally();\n setQuotaKind('session');\n setShowQuotaExhausted(true);\n }}\n />\n\n {/* Remove-plugin confirmation (replaces window.confirm) */}\n <ConfirmDialog\n isOpen={pluginRemoveTarget !== null}\n onClose={() => setPluginRemoveTarget(null)}\n onConfirm={() => {\n const packageName = pluginRemoveTarget;\n setPluginRemoveTarget(null);\n if (packageName) void performRemovePlugin(packageName);\n }}\n title={tr('agentDetails.removePluginTitle', 'Remove plugin')}\n message={tr('agentDetails.removePluginConfirm', 'Remove plugin {{packageName}}?', {\n packageName: pluginRemoveTarget ?? '',\n })}\n confirmLabel={tr('sessions.remove', 'Remove')}\n variant=\"danger\"\n />\n\n {/* Update-plugin version prompt (replaces window.prompt).\n Blank = latest; Cancel aborts. */}\n <PromptDialog\n isOpen={pluginUpdateTarget !== null}\n onClose={() => setPluginUpdateTarget(null)}\n onSubmit={(value) => {\n const packageName = pluginUpdateTarget;\n setPluginUpdateTarget(null);\n if (packageName) void performUpdatePlugin(packageName, value || undefined);\n }}\n title={tr('agentDetails.updatePluginTitle', 'Update plugin')}\n description={tr(\n 'agentDetails.updatePluginPrompt',\n 'Update {{packageName}} to which version? Leave blank for latest.',\n { packageName: pluginUpdateTarget ?? '' }\n )}\n label={tr('agentDetails.updatePluginVersionLabel', 'Version')}\n placeholder={tr('agentDetails.updatePluginVersionPlaceholder', 'latest')}\n confirmLabel={tr('agentDetails.update', 'Update')}\n />\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title={getQuotaDialogContent(quotaKind).title}\n message={getQuotaDialogContent(quotaKind).message}\n />\n\n <ConfirmDialog\n isOpen={showRemoveDialog}\n onClose={() => {\n if (!removingAgent) setShowRemoveDialog(false);\n }}\n onConfirm={handleConfirmRemoveAgent}\n title={tr('agentDetails.removeAgentTitle', 'Remove agent')}\n message={tr('agentDetails.removeAgentMessage', 'Remove “{{name}}” from this workspace.', {\n name: agent.name,\n })}\n confirmLabel={tr('sessions.remove', 'Remove')}\n variant=\"danger\"\n isLoading={removingAgent}\n >\n <div className=\"space-y-2\">\n <label className=\"flex items-start gap-3 p-3 rounded-md bg-bg-sunken border border-border-default cursor-pointer\">\n <Checkbox\n checked={uninstallFromTarget}\n disabled={removingAgent}\n onCheckedChange={(c) => setUninstallFromTarget(c === true)}\n className=\"mt-0.5\"\n />\n <span className=\"min-w-0\">\n <span className=\"block text-sm font-medium text-text-primary\">\n {tr('agentDetails.uninstallFromTarget', 'Uninstall agent from the target')}\n </span>\n <span className=\"block text-xs text-text-secondary mt-0.5\">\n {tr(\n 'agentDetails.uninstallFromTargetDesc',\n 'Also wipe all agent state + VibeControls plugins and uninstall the agent from the host machine. This cannot be undone.'\n )}\n </span>\n </span>\n </label>\n <label className=\"flex items-start gap-3 p-3 rounded-md bg-bg-sunken border border-border-default cursor-pointer\">\n <Checkbox\n checked={deleteTargetWithAgent}\n disabled={removingAgent}\n onCheckedChange={(c) => setDeleteTargetWithAgent(c === true)}\n className=\"mt-0.5\"\n />\n <span className=\"min-w-0\">\n <span className=\"block text-sm font-medium text-text-primary\">\n {tr('agentDetails.deleteTarget', 'Delete the mapped target')}\n </span>\n <span className=\"block text-xs text-text-secondary mt-0.5\">\n {tr(\n 'agentDetails.deleteTargetDesc',\n 'Also delete the target this agent is mapped to. Any other agents on the same target are removed too. This cannot be undone.'\n )}\n </span>\n </span>\n </label>\n </div>\n </ConfirmDialog>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentDetailsPage.js","names":[],"sources":["../../src/pages/AgentDetailsPage.tsx"],"sourcesContent":["import { useState, useRef, useEffect, useCallback, useMemo } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { isNative, nativeCopyText, nativeToast } from '../utils/nativeBridge';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useParams } from 'react-router-dom';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { AgentBackupTab } from '@/components/agents/backup/AgentBackupTab';\nimport { ConnectionLogPanel } from '@/components/agents/ConnectionLogPanel';\nimport { AgentMaintenanceTab } from '@/components/agents/AgentMaintenanceTab';\nimport { AgentConnectionRecovery } from '@/components/agents/AgentConnectionRecovery';\nimport { AgentStorageConfig } from '@/components/agents/AgentStorageConfig';\nimport { AliasManager } from '@/components/aliases/AliasManager';\nimport {\n ArrowLeft,\n Database,\n Server,\n Play,\n Square,\n Settings,\n Trash2,\n Terminal,\n Zap,\n Cpu,\n HardDrive,\n Clock,\n CheckCircle2,\n RefreshCw,\n Eye,\n EyeOff,\n Key,\n Copy,\n Check,\n Info,\n Puzzle,\n Download,\n X,\n Loader2,\n Shield,\n Monitor,\n FileText,\n Tag,\n ScrollText,\n Command,\n Users,\n Plus,\n Edit3,\n Bot,\n Wrench,\n ArrowUpCircle,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatusBadge,\n EmptyState,\n ErrorState,\n QuotaExhaustedDialog,\n ConfirmDialog,\n ActionMenu,\n MotionTabPanel,\n PromptDialog,\n SearchInput,\n} from '@/components/shared';\nimport { Button, Checkbox, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { Tabs, type TabItem } from '@burdenoff/fe-libs/chrome';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { notify } from '@/lib/toast';\nimport {\n PluginUIPanel,\n type UIPluginOption,\n OpenInBrowserEditorButton,\n AgentStatusBadge,\n RemoteTaskPermissionsCard,\n GatewayAuthCard,\n ConfigureAgentDialog,\n CreateSessionDialog,\n} from '@/components/agents';\nimport { deriveAgentStatus, type AgentHealthProbe } from '@/utils/agentStatus';\nimport { LogViewer } from '@/components/logs/LogViewer';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { ProfilesPanel } from '@/components/agent-manager';\nimport { NotesTabPanel } from '@/components/notes';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useQueryBackedTab } from '@/hooks/useQueryBackedTab';\nimport {\n isQuotaExhaustedError,\n markQuotaHandledLocally,\n getQuotaDialogContent,\n} from '@/utils/quotaUtils';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport {\n type DiscoveredContribution,\n type MountTunnel,\n usePluginContributions,\n} from '@burdenoff/fe-libs/shared/plugins';\nimport { mintScopedIframeToken } from '@/services/iframeToken';\nimport { AddTabDialog } from '@/components/plugins/AddTabDialog';\nimport { PluginIframeMount } from '@/components/plugins/PluginIframeMount';\nimport {\n useGetAgentQuery,\n useUpdateAgentMutation,\n useDeleteAgentMutation,\n useCheckSessionHealthMutation,\n useAgentPluginsLazyQuery,\n useInstallAgentPluginMutation,\n useRemoveAgentPluginMutation,\n useUpdateAgentPluginMutation,\n useSetupAgentGatewayAuthMutation,\n useVibecontrolsAgentHealthProbesQuery,\n useVibecontrolsAgentHealthProbeStreamSubscription,\n useStartVibecontrolsAgentMutation,\n useStopVibecontrolsAgentMutation,\n useAgentSecretRevealLazyQuery,\n} from '@/generated/wspace-operations';\n\n// The Gateway Auth (OAuth2) card lives in\n// `@/components/agents/GatewayAuthCard` — extracted from this page.\n\n// `AgentPluginIframeBody` was the page-local helper that minted a\n// token + rendered an iframe without refresh / error handling. The\n// consolidated logic now lives in `@/components/plugins/PluginIframeMount`.\n\ntype AgentTab =\n | 'overview'\n | 'profiles'\n | 'security'\n | 'plugins'\n | 'sessions'\n | 'logs'\n | 'connection'\n | 'vibes'\n | 'notes'\n | 'aliases'\n | 'backup'\n | 'maintenance';\n\n// Module-scope so the reference is stable across renders (useMemo dep).\nconst AGENT_TAB_IDS: readonly AgentTab[] = [\n 'overview',\n 'profiles',\n 'security',\n 'plugins',\n 'sessions',\n 'logs',\n 'connection',\n 'vibes',\n 'notes',\n 'aliases',\n 'backup',\n 'maintenance',\n];\n\n/**\n * Agent details page - shows comprehensive information about a single agent\n */\nexport function AgentDetailsPage() {\n const { id } = useParams<{ id: string }>();\n const { basePath: basePathProp } = useVibeControls();\n const basePath = basePathProp || '';\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const tr = useTr();\n\n const [updateAgent] = useUpdateAgentMutation({ refetchQueries: ['GetAgent'] });\n // refetchQueries + awaitRefetchQueries make sure the agents list\n // (and the cached initial-data slice the AgentsPage falls back to\n // before the live query loads) drop the deleted row BEFORE we\n // navigate back, so the list doesn't briefly re-render with the\n // gone agent. cache.evict on the normalized entry covers other\n // queries that happen to reference this agent ID.\n const [deleteAgent] = useDeleteAgentMutation({\n refetchQueries: ['ListAgents', 'VibeControlsInitialData'],\n awaitRefetchQueries: true,\n update(cache, _result, { variables }) {\n const id = variables?.id;\n if (!id) return;\n cache.evict({ id: cache.identify({ __typename: 'VibecontrolsAgent', id }) });\n cache.gc();\n },\n });\n const [checkSessionHealth] = useCheckSessionHealthMutation({ refetchQueries: ['GetAgent'] });\n const [startVibecontrolsAgent] = useStartVibecontrolsAgentMutation();\n const [stopVibecontrolsAgent] = useStopVibecontrolsAgentMutation();\n // Per-action in-flight state so the Start/Stop button can show a spinner +\n // disable while the lifecycle call is running (it can take a few seconds to\n // reach the agent over its tunnel).\n const [lifecycleAction, setLifecycleAction] = useState<'start' | 'stop' | null>(null);\n\n // ── Sync active context ────────────────────────────────────────────────\n const setActiveAgentId = useVibeControlsStore((s) => s.setActiveAgentId);\n useEffect(() => {\n if (id) setActiveAgentId(id);\n }, [id, setActiveAgentId]);\n\n // ── Tab Navigation ───────────────────────────────────────────────────\n // `AgentTab` + `AGENT_TAB_IDS` are hoisted to module scope so the array\n // reference is stable across renders (memo dep correctness).\n // Active-tab declaration is deferred to after `iframeContributions`\n // (further down in this component) so the valid id set can include\n // both the static `agentTabIds` AND discovered plugin ids — see\n // the same pattern in VibeDetailsPage.\n\n // Configuration dialog open state — form state lives in ConfigureAgentDialog.\n const [showConfigModal, setShowConfigModal] = useState(false);\n\n // Inline rename state — lets operators rename the agent from the page\n // header without opening the full Configure modal. Click the pencil →\n // input swaps in. Cancel reverts to the stored name; Save fires the\n // existing UpdateAgent mutation and refetches GetAgent so every reader\n // on the page reflects the new name in the same render.\n const [renaming, setRenaming] = useState(false);\n const [renameValue, setRenameValue] = useState('');\n const [renameSaving, setRenameSaving] = useState(false);\n const [renameError, setRenameError] = useState<string | null>(null);\n const renameInputRef = useRef<HTMLInputElement>(null);\n\n const [showApiKey, setShowApiKey] = useState(false);\n const [apiKeyCopied, setApiKeyCopied] = useState(false);\n // F4 Wave B: the long-lived agent secret is no longer prefetched with\n // every GetAgent. Reveal lazily on operator click; svc-side RBAC +\n // audit gates the read.\n const [revealedApiKey, setRevealedApiKey] = useState<string | null>(null);\n const [revealLoading, setRevealLoading] = useState(false);\n const [revealError, setRevealError] = useState<string | null>(null);\n const [fetchAgentSecret] = useAgentSecretRevealLazyQuery({ fetchPolicy: 'network-only' });\n\n // Create Session dialog open state — form state lives in CreateSessionDialog\n // (ResponsiveDialog supplies Escape, focus trap and initial focus).\n const [showSessionModal, setShowSessionModal] = useState(false);\n\n // Fetch agent by ID from GraphQL API\n const { data, loading, error, refetch } = useGetAgentQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n // Get agent from response\n const agent = data?.vibecontrolsAgent;\n\n // Live deep-health probe — same source the agent cards use, so the detail\n // page's status badge matches the list. Without this the badge derived from\n // the sticky `isActive` flag, showing \"Active\" for an agent the card (and\n // reality) reports DEGRADED/stopped. Workspace-scoped query primes the cache;\n // the subscription pushes sub-second updates.\n const probeQuery = useVibecontrolsAgentHealthProbesQuery({\n fetchPolicy: 'cache-and-network',\n nextFetchPolicy: 'cache-first',\n notifyOnNetworkStatusChange: true,\n skip: !id,\n pollInterval: 30_000,\n });\n const { data: probeStreamData } = useVibecontrolsAgentHealthProbeStreamSubscription({\n skip: !id,\n });\n const probesLoading = probeQuery.data === undefined && probeStreamData === undefined;\n const probesById = useMemo(() => {\n const m = new Map<string, AgentHealthProbe>();\n const probes =\n probeStreamData?.vibecontrolsAgentHealthProbeStream ??\n probeQuery.data?.vibecontrolsAgentHealthProbes ??\n [];\n for (const p of probes) {\n m.set(p.agentId, { agentId: p.agentId, healthy: p.healthy, errorMessage: p.errorMessage });\n }\n return m;\n }, [probeQuery.data, probeStreamData]);\n\n // Single source of truth for the agent's displayed status — live /health/ready\n // probe + lifecycleState, NOT the sticky isActive flag. Drives both the status\n // badge and the Start/Stop toggle so they always agree with the agent cards.\n const derivedStatus = useMemo(\n () =>\n agent\n ? deriveAgentStatus(\n {\n id: agent.id,\n isActive: agent.isActive,\n metadata: agent.metadata as Record<string, unknown> | null,\n },\n probesById,\n { probesLoading }\n )\n : 'REGISTERING',\n [agent, probesById, probesLoading]\n );\n\n // Only offer Start when the agent is known NOT to be running (STOPPED or\n // OFFLINE). For ACTIVE — and also DEGRADED / REGISTERING, where the process\n // can still be live during probe warm-up or transport errors — show Stop, so\n // an operator can always stop a live-but-unhealthy agent instead of firing a\n // start that fails as \"already running\".\n const showStartButton = derivedStatus === 'STOPPED' || derivedStatus === 'OFFLINE';\n\n // Phase 5B — resolve the active profile name for this agent. Used to\n // build canonical `/api/profiles/<name>/<rest>` paths in child tabs\n // (LogViewer, OpenInBrowserEditorButton, AgentBackupTab).\n const { active: activeProfile } = useActiveAgentProfile(agent?.id ?? null);\n\n // Plugins are agent-wide (BOFF-2612): the npm package install and the\n // \"available\" catalog are already shared across all profiles on the\n // agent; only plugin DATA (notes/sessions/etc.) stays per-profile.\n // No per-profile gate on Install/Remove anymore.\n\n // ── iframe-mount discovery (PR-8) ────────────────────────────────────\n // Gated by `vibes.tabs.iframe-host` (same flag the VibeDetailsPage\n // uses — one switch for both surfaces). When on, the host queries the\n // agent's tunnel for contributions targeting the `agent.detailTab`\n // mount point and renders them as additional tabs.\n const iframeHostFlag = useFeatureFlagDetails('vibes.tabs.iframe-host');\n const enableIframeHost = iframeHostFlag?.value === true;\n\n const iframePluginTunnels = useMemo<MountTunnel[]>(() => {\n if (!enableIframeHost || !agent?.tunnelUrl) return [];\n const agentIdForToken = agent.id;\n const profile = activeProfile ?? 'default';\n return [\n {\n tunnelUrl: agent.tunnelUrl,\n profile,\n label: agent.name ?? agent.id,\n mintIframeToken: async ({ pathPrefixes }) => {\n return mintScopedIframeToken(agentIdForToken, profile, pathPrefixes);\n },\n },\n ];\n }, [enableIframeHost, agent?.id, agent?.tunnelUrl, agent?.name, activeProfile]);\n\n const iframeContributions = usePluginContributions(iframePluginTunnels, {\n mountPoint: 'agent.detailTab',\n enabled: enableIframeHost && iframePluginTunnels.length > 0,\n });\n\n // Active-tab id set = static AGENT_TAB_IDS ∪ discovered plugin ids.\n // See VibeDetailsPage for the rationale.\n const validAgentTabIds = useMemo<readonly string[]>(() => {\n const fromContribs = iframeContributions.data\n .filter((c) => c.contribution.mountPoint === 'agent.detailTab')\n .map((c) => c.contribution.id);\n return [...(AGENT_TAB_IDS as readonly string[]), ...fromContribs];\n }, [iframeContributions.data]);\n const [activeTab, setActiveTab] = useQueryBackedTab<string>(validAgentTabIds, 'overview');\n\n const [addTabOpen, setAddTabOpen] = useState(false);\n\n // On mount, run a health check to sync stale session states with agent reality\n const healthCheckRanRef = useRef(false);\n useEffect(() => {\n if (agent && !healthCheckRanRef.current) {\n healthCheckRanRef.current = true;\n checkSessionHealth().catch(() => {\n // Best-effort — don't block the page if health check fails\n });\n }\n }, [agent, checkSessionHealth]);\n\n // Auto-recovery for agents stuck in `awaiting-config`.\n //\n // Symptom (why this exists): a freshly-booted agent loads its plugin\n // catalog but defers `onServerStart` — and therefore session-provider\n // registration — until the dashboard POSTs OAuth credentials to\n // /api/agent/gateway-auth. If the initial push failed (network blip,\n // auth-svc transient, page closed mid-flight) the agent stays stuck\n // and the badge reads \"Degraded — no session provider registered\".\n //\n // Fix: when the page loads and finds (a) an agent linked to an OAuth\n // app already, (b) reachable via tunnelUrl + agentApiKey, fire the same\n // recovery the manual \"Re-push credentials\" button uses —\n // `setupAgentGatewayAuth`. It rotates the existing OAuth app's secret on\n // auth-svc and re-pushes the fresh credentials to the agent, deriving the\n // gateway URLs/scopes server-side from the request context. The agent\n // finalizes and registers providers on receipt; self-heals the page\n // without a manual click.\n //\n // NOTE: we deliberately do NOT call `pushAgentGatewayAuth({ agentId })`\n // here — that mutation requires the full OAuth client secret in its input\n // (a server-side secret the browser does not hold), so a bare-agentId call\n // produced an INTERNAL_SERVER_ERROR. The OAuth secret is only known at\n // create/rotate time, so recovery must go through setup (rotate + push).\n const [recoverGatewayAuth] = useSetupAgentGatewayAuthMutation();\n const autoRecoveryRanRef = useRef(false);\n useEffect(() => {\n if (!agent || autoRecoveryRanRef.current) return;\n const hasApp = Boolean(agent.appId && agent.appClientId);\n const reachable = Boolean(agent.tunnelUrl);\n if (!hasApp || !reachable) return;\n autoRecoveryRanRef.current = true;\n recoverGatewayAuth({ variables: { input: { agentId: agent.id } } })\n .then(() => {\n // Re-fetch so badge + reason update once the agent finalizes.\n refetch().catch(() => {\n /* badge will refresh on next heartbeat */\n });\n })\n .catch(() => {\n // Silent — manual \"Re-push credentials\" button remains available.\n });\n }, [agent, recoverGatewayAuth, refetch]);\n\n // ── Plugin Management (via backend GraphQL proxy) ──────────────────\n interface InstalledPlugin {\n packageName: string;\n pluginName: string;\n version: string;\n installedAt: string;\n loaded: boolean;\n description?: string;\n tags?: string[];\n isCore: boolean;\n cliCommand?: string;\n apiPrefix?: string;\n hasUI: boolean;\n hasCliSetup: boolean;\n hasServerStart: boolean;\n hasServerStop: boolean;\n hasServerReady: boolean;\n hasProviders: boolean;\n }\n interface AvailablePlugin {\n packageName: string;\n name: string;\n description: string;\n version: string;\n tags: string[];\n installed: boolean;\n }\n\n const [installedPlugins, setInstalledPlugins] = useState<InstalledPlugin[]>([]);\n const [availablePlugins, setAvailablePlugins] = useState<AvailablePlugin[]>([]);\n const [pluginsLoading, setPluginsLoading] = useState(true);\n const [pluginActionLoading, setPluginActionLoading] = useState<string | null>(null);\n const [pluginError, setPluginError] = useState<string | null>(null);\n // Package currently pending confirmation/version input — drives the\n // ConfirmDialog (remove) and PromptDialog (update) that replaced the\n // old window.confirm / window.prompt calls.\n const [pluginRemoveTarget, setPluginRemoveTarget] = useState<string | null>(null);\n const [pluginUpdateTarget, setPluginUpdateTarget] = useState<string | null>(null);\n // Remove-agent dialog (declared with the other hooks, before any early\n // return, to satisfy rules-of-hooks).\n const [showRemoveDialog, setShowRemoveDialog] = useState(false);\n const [uninstallFromTarget, setUninstallFromTarget] = useState(false);\n const [deleteTargetWithAgent, setDeleteTargetWithAgent] = useState(false);\n const [removingAgent, setRemovingAgent] = useState(false);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n // Which quota the open dialog is reporting on. The plugin-install flow\n // opens the same dialog and was here first, so it's the default.\n const [quotaKind, setQuotaKind] = useState<'plugin' | 'session'>('plugin');\n // Tracks the last (agent, profile) pair we fetched plugins for, so a\n // profile switch re-fetches the profile-scoped plugin list instead of\n // showing the previous profile's plugins.\n const pluginsFetchedRef = useRef<string | null>(null);\n\n // ── Plugin Sub-Tabs & Search ──────────────────────────────────────\n type PluginSubTab = 'installed' | 'available' | 'store' | 'ui-plugins';\n const [pluginSubTab, setPluginSubTab] = useState<PluginSubTab>('installed');\n const [pluginSearchQuery, setPluginSearchQuery] = useState('');\n\n // ── Store Plugins ─────────────────────────────────────────────────\n type StoreEntitlement = {\n id: string;\n productId: string;\n licenseKey: string | null;\n product: {\n id: string;\n name: string;\n slug: string;\n icon: string | null;\n description: string | null;\n manifest: Record<string, unknown> | null;\n } | null;\n };\n const [storePlugins, setStorePlugins] = useState<StoreEntitlement[]>([]);\n const [storePluginsLoading, setStorePluginsLoading] = useState(false);\n const [storePluginsError, setStorePluginsError] = useState<string | null>(null);\n const [storeInstallingId, setStoreInstallingId] = useState<string | null>(null);\n\n // Backend-proxied plugin operations\n const [fetchAgentPlugins] = useAgentPluginsLazyQuery();\n const [installPluginMutation] = useInstallAgentPluginMutation();\n const [removePluginMutation] = useRemoveAgentPluginMutation();\n const [updatePluginMutation] = useUpdateAgentPluginMutation();\n\n const fetchPlugins = useCallback(async () => {\n if (!agent?.id) return;\n setPluginsLoading(true);\n setPluginError(null);\n try {\n const { data } = await fetchAgentPlugins({\n variables: { agentId: agent.id, profile: activeProfile },\n });\n const result = data?.agentPlugins;\n if (result) {\n const installed = (result.installed || []).map(\n (p: {\n packageName: string;\n pluginName: string;\n version: string;\n installedAt?: string | null;\n loaded: boolean;\n description?: string | null;\n tags?: (string | null)[] | null;\n isCore: boolean;\n cliCommand?: string | null;\n apiPrefix?: string | null;\n hasUI: boolean;\n hasCliSetup: boolean;\n hasServerStart: boolean;\n hasServerStop: boolean;\n hasServerReady: boolean;\n hasProviders: boolean;\n }) => ({\n packageName: p.packageName,\n pluginName: p.pluginName,\n version: p.version,\n installedAt: p.installedAt || '',\n loaded: p.loaded,\n description: p.description ?? undefined,\n tags: (p.tags?.filter(Boolean) as string[]) ?? undefined,\n isCore: p.isCore,\n cliCommand: p.cliCommand ?? undefined,\n apiPrefix: p.apiPrefix ?? undefined,\n hasUI: p.hasUI,\n hasCliSetup: p.hasCliSetup,\n hasServerStart: p.hasServerStart,\n hasServerStop: p.hasServerStop,\n hasServerReady: p.hasServerReady,\n hasProviders: p.hasProviders,\n })\n );\n const available = (result.available || []).map(\n (p: {\n packageName: string;\n name: string;\n description: string;\n version: string;\n tags: string[];\n installed: boolean;\n }) => ({\n packageName: p.packageName,\n name: p.name,\n description: p.description,\n version: p.version,\n tags: [...p.tags],\n installed: p.installed,\n })\n );\n setInstalledPlugins(installed);\n setAvailablePlugins(available);\n if (result.error) {\n setPluginError(result.error);\n }\n }\n } catch {\n // Agent unreachable — show error state\n setPluginError('Agent unreachable — unable to fetch plugin list');\n } finally {\n setPluginsLoading(false);\n }\n }, [agent?.id, fetchAgentPlugins, activeProfile]);\n\n // Attempt to fetch live plugins when agent data loads.\n // If the agent is unreachable, an error state is shown instead of fake data.\n useEffect(() => {\n if (!agent?.id) return;\n const fetchKey = `${agent.id}:${activeProfile}`;\n if (pluginsFetchedRef.current !== fetchKey) {\n pluginsFetchedRef.current = fetchKey;\n fetchPlugins();\n }\n }, [agent?.id, activeProfile, fetchPlugins]);\n\n const handleInstallPlugin = useCallback(\n async (packageName: string) => {\n if (!agent?.id) return;\n setPluginActionLoading(packageName);\n try {\n const { data } = await installPluginMutation({\n variables: { agentId: agent.id, packageName },\n });\n if (data?.installAgentPlugin?.success) {\n await fetchPlugins();\n } else {\n throw new Error(\n data?.installAgentPlugin?.error || tr('agentDetails.installFailed', 'Install failed')\n );\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setQuotaKind('plugin');\n setShowQuotaExhausted(true);\n return;\n }\n\n setPluginError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.pluginInstallFailed', 'Failed to install plugin')\n );\n setTimeout(() => setPluginError(null), 5000);\n } finally {\n setPluginActionLoading(null);\n }\n },\n [agent?.id, installPluginMutation, fetchPlugins, tr]\n );\n\n // Opens the remove-plugin ConfirmDialog (replaces window.confirm).\n const handleRemovePlugin = useCallback((packageName: string) => {\n setPluginRemoveTarget(packageName);\n }, []);\n\n const performRemovePlugin = useCallback(\n async (packageName: string) => {\n if (!agent?.id) return;\n setPluginActionLoading(packageName);\n try {\n const { data } = await removePluginMutation({\n variables: { agentId: agent.id, packageName },\n });\n if (data?.removeAgentPlugin?.success) {\n await fetchPlugins();\n } else {\n throw new Error(\n data?.removeAgentPlugin?.error || tr('agentDetails.removeFailed', 'Remove failed')\n );\n }\n } catch (err) {\n setPluginError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.pluginRemoveFailed', 'Failed to remove plugin')\n );\n setTimeout(() => setPluginError(null), 5000);\n } finally {\n setPluginActionLoading(null);\n }\n },\n [agent?.id, removePluginMutation, fetchPlugins, tr]\n );\n\n // Opens the update-plugin PromptDialog (replaces window.prompt).\n // Optional pinned version — Cancel aborts; blank = latest.\n const handleUpdatePlugin = useCallback((packageName: string) => {\n setPluginUpdateTarget(packageName);\n }, []);\n\n const performUpdatePlugin = useCallback(\n async (packageName: string, version: string | undefined) => {\n if (!agent?.id) return;\n setPluginActionLoading(packageName);\n try {\n const { data } = await updatePluginMutation({\n variables: { agentId: agent.id, packageName, version: version ?? null },\n });\n const proxy = data?.updateAgentPlugin;\n if (proxy?.ok) {\n await fetchPlugins();\n } else {\n throw new Error(proxy?.error || tr('agentDetails.updateFailed', 'Plugin update failed'));\n }\n } catch (err) {\n setPluginError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.pluginUpdateFailed', 'Failed to update plugin')\n );\n setTimeout(() => setPluginError(null), 5000);\n } finally {\n setPluginActionLoading(null);\n }\n },\n [agent?.id, updatePluginMutation, fetchPlugins, tr]\n );\n\n // Get sessions and vibes from the agent response (already included in the query)\n const agentSessions = agent?.sessions || [];\n const agentVibes = agent?.vibes || [];\n\n // ── Store plugin fetch / install ──────────────────────────────────\n const fetchStorePlugins = useCallback(async () => {\n setStorePluginsLoading(true);\n setStorePluginsError(null);\n try {\n const response = await fetch(\n (typeof window !== 'undefined' &&\n (window as { __BURDENOFF_GLOBAL_GATEWAY__?: string }).__BURDENOFF_GLOBAL_GATEWAY__) ||\n '/global/graphql',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n query: `query MyStorePlugins {\n myEntitlements {\n id\n productId\n licenseKey\n product {\n id\n name\n slug\n icon\n description\n manifest\n }\n }\n }`,\n }),\n }\n );\n const body = (await response.json()) as {\n data?: { myEntitlements?: StoreEntitlement[] };\n errors?: Array<{ message: string }>;\n };\n if (body.errors?.length)\n throw new Error(body.errors[0]?.message ?? 'Failed to load store plugins');\n const plugins = (body.data?.myEntitlements ?? []).filter((e) => {\n const manifest = e.product?.manifest as Record<string, unknown> | null;\n if (!manifest) return false;\n const itemType = (manifest['itemType'] as string | undefined)?.toLowerCase();\n return itemType === 'vibemodule' || itemType === 'plugin';\n });\n setStorePlugins(plugins);\n } catch (err) {\n setStorePluginsError(err instanceof Error ? err.message : 'Failed to load store plugins');\n } finally {\n setStorePluginsLoading(false);\n }\n }, []);\n\n useEffect(() => {\n if (pluginSubTab === 'store' && storePlugins.length === 0 && !storePluginsLoading) {\n void fetchStorePlugins();\n }\n }, [pluginSubTab, storePlugins.length, storePluginsLoading, fetchStorePlugins]);\n\n const handleInstallStorePlugin = useCallback(\n async (productId: string, licenseKey: string | null) => {\n if (!agent?.id) return;\n setStoreInstallingId(productId);\n try {\n const response = await fetch(\n (typeof window !== 'undefined' &&\n (window as { __BURDENOFF_WSPACE_GATEWAY__?: string }).__BURDENOFF_WSPACE_GATEWAY__) ||\n '/workspaces/graphql',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n query: `mutation InstallStorePlugin($agentId: ID!, $productId: ID!, $licenseKey: String) {\n installStorePluginOnAgent(agentId: $agentId, productId: $productId, licenseKey: $licenseKey) {\n success\n error\n }\n }`,\n variables: { agentId: agent.id, productId, licenseKey },\n }),\n }\n );\n const body = (await response.json()) as {\n data?: {\n installStorePluginOnAgent?: { success: boolean; error: string | null };\n };\n errors?: Array<{ message: string }>;\n };\n if (body.errors?.length) throw new Error(body.errors[0]?.message ?? 'Install failed');\n const result = body.data?.installStorePluginOnAgent;\n if (!result?.success) {\n throw new Error(result?.error ?? 'Install failed');\n }\n await fetchPlugins();\n setPluginSubTab('installed');\n } catch (err) {\n setStorePluginsError(err instanceof Error ? err.message : 'Install failed');\n } finally {\n setStoreInstallingId(null);\n }\n },\n [agent?.id, fetchPlugins]\n );\n\n // Handle ?install=<productId>&licenseKey=... on page load — automatic install flow\n // when the user arrives here from OrderConfirmationPage\n useEffect(() => {\n if (typeof window === 'undefined' || !agent?.id) return;\n const params = new URLSearchParams(window.location.search);\n const productId = params.get('install');\n const licenseKey = params.get('licenseKey');\n if (productId && productId !== storeInstallingId) {\n setPluginSubTab('store');\n void handleInstallStorePlugin(productId, licenseKey);\n // Clear the query params after initiating\n const url = new URL(window.location.href);\n url.searchParams.delete('install');\n url.searchParams.delete('licenseKey');\n window.history.replaceState({}, '', url.toString());\n }\n }, [agent?.id, handleInstallStorePlugin, storeInstallingId]);\n\n const handleRevealApiKey = useCallback(async (): Promise<string | null> => {\n if (!agent?.id) return null;\n if (revealedApiKey) return revealedApiKey;\n setRevealLoading(true);\n setRevealError(null);\n try {\n const res = await fetchAgentSecret({ variables: { agentId: agent.id } });\n const key = res.data?.vibecontrolsAgentSecretReveal ?? null;\n if (!key) {\n setRevealError('No API key configured');\n return null;\n }\n setRevealedApiKey(key);\n return key;\n } catch (err) {\n setRevealError(err instanceof Error ? err.message : 'Reveal failed');\n return null;\n } finally {\n setRevealLoading(false);\n }\n }, [agent?.id, revealedApiKey, fetchAgentSecret]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions…</p>\n </div>\n );\n if (\n permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !hasPermission(VIBE_PERMISSIONS.AGENT_READ)\n )\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleBack = () => {\n navigate('/agents');\n };\n\n // Real lifecycle control: start/stop the agent process via the backend proxy\n // (POST /agent/lifecycle/{start,stop} on the agent), not a metadata write.\n // Shows a spinner + disables the button while in flight and surfaces the\n // outcome as a toast; refreshes the agent record + live health probe so the\n // badge and the Start/Stop toggle flip immediately.\n const runLifecycle = async (action: 'start' | 'stop') => {\n if (!agent?.id || lifecycleAction) return;\n setLifecycleAction(action);\n try {\n const res =\n action === 'start'\n ? await startVibecontrolsAgent({ variables: { agentId: agent.id } })\n : await stopVibecontrolsAgent({ variables: { agentId: agent.id } });\n const payload =\n action === 'start' ? res.data?.startVibecontrolsAgent : res.data?.stopVibecontrolsAgent;\n // Only treat an explicitly-successful payload as success. A null payload\n // (network hiccup / unexpected schema shape) must not fall through to the\n // success toast.\n if (!payload || payload.success === false) {\n throw new Error(payload?.error || `Failed to ${action} agent`);\n }\n notify.success(\n action === 'start'\n ? tr('agentDetails.agentStarted', 'Agent started')\n : tr('agentDetails.agentStopped', 'Agent stopped')\n );\n // Snappy refresh — don't wait for the 30s probe poll / heartbeat sweep.\n await Promise.all([refetch(), probeQuery.refetch()]).catch(() => {});\n } catch (err) {\n const raw = err instanceof Error ? err.message : '';\n // The backend reaches the agent only over its tunnel. If the tunnel is\n // down (common after a stop, or if the host went away) the call can't\n // land — surface an actionable message instead of a raw fetch error.\n const unreachable =\n /unable to connect|fetch failed|timed? ?out|ECONN|agent returned 5|network/i.test(raw);\n notify.error(\n unreachable\n ? tr(\n 'agentDetails.agentUnreachable',\n `Couldn't reach the agent to ${action} it — it may be offline or its tunnel is down. Start it from the host with: vibe start`\n )\n : raw || tr('agentDetails.lifecycleFailed', 'Failed to {{action}} agent', { action })\n );\n } finally {\n setLifecycleAction(null);\n }\n };\n\n const handleStartAgent = () => void runLifecycle('start');\n const handleStopAgent = () => void runLifecycle('stop');\n const handleAfterDestructiveNuke = async () => {\n await Promise.all([refetch(), probeQuery.refetch()]).catch(() => {});\n navigate('/agents');\n };\n\n const handleConfigureAgent = () => {\n setShowConfigModal(true);\n };\n\n const handleRenameSave = async (): Promise<void> => {\n if (!agent?.id) return;\n const next = renameValue.trim();\n if (!next || next === agent.name) {\n setRenaming(false);\n return;\n }\n setRenameSaving(true);\n setRenameError(null);\n try {\n const result = await updateAgent({\n variables: { id: agent.id, input: { name: next } },\n });\n if (result.error) {\n throw result.error;\n }\n setRenaming(false);\n } catch (err) {\n // Most common failure mode: the (workspaceId, name) unique\n // constraint in wspace-vibecontrols-svc rejects a duplicate.\n // Surface the message inline so the operator can pick a\n // different name without losing what they typed.\n setRenameError(err instanceof Error ? err.message : String(err));\n } finally {\n setRenameSaving(false);\n }\n };\n\n const handleRenameCancel = (): void => {\n setRenaming(false);\n setRenameValue('');\n setRenameError(null);\n };\n\n const handleCopyApiKey = async () => {\n const key = await handleRevealApiKey();\n if (!key) return;\n try {\n await nativeCopyText(key);\n if (isNative()) void nativeToast('API key copied');\n setApiKeyCopied(true);\n setTimeout(() => setApiKeyCopied(false), 2000);\n } catch {\n // Fallback for non-HTTPS contexts\n console.error('Failed to copy API key');\n }\n };\n\n const handleToggleShowApiKey = async () => {\n if (!showApiKey) {\n await handleRevealApiKey();\n }\n setShowApiKey((v) => !v);\n };\n\n const handleRemoveAgent = () => {\n if (!agent?.id) return;\n setUninstallFromTarget(false);\n setDeleteTargetWithAgent(false);\n setShowRemoveDialog(true);\n };\n\n const handleConfirmRemoveAgent = async () => {\n if (!agent?.id) return;\n setRemovingAgent(true);\n try {\n await deleteAgent({\n variables: { id: agent.id, uninstallFromTarget, deleteTarget: deleteTargetWithAgent },\n });\n setShowRemoveDialog(false);\n handleBack();\n } catch (err) {\n console.error('Failed to remove agent:', err);\n } finally {\n setRemovingAgent(false);\n }\n };\n\n const handleSessionClick = (sessionId: string) => {\n navigate(`/sessions/${sessionId}`);\n };\n\n const handleStartSession = () => {\n setShowSessionModal(true);\n };\n\n const handleVibeClick = (vibeId: string) => {\n navigate(`/vibes/${vibeId}`);\n };\n\n const formatTimestamp = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('health.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('health.justNow', 'Just now');\n if (diff < 3600000) {\n return tr('agentDetails.minutesAgo', '{{count}}m ago', {\n count: Math.floor(diff / 60000),\n });\n }\n if (diff < 86400000) {\n return tr('agentDetails.hoursAgo', '{{count}}h ago', {\n count: Math.floor(diff / 3600000),\n });\n }\n return new Date(timestamp).toLocaleDateString();\n };\n\n // Loading state - wrapped in MotionPage to prevent unmount/remount animation replay\n if (loading && !agent) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n // Error state\n if (error && !agent) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </button>\n <ErrorState\n title={tr('agentDetails.failedLoad', 'Failed to load agent')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n // Not found state\n if (!agent) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </button>\n <EmptyState\n icon={<Server className=\"size-8 text-text-secondary\" />}\n title={tr('agentDetails.notFound', 'Agent not found')}\n description={`${tr('agentDetails.notFoundDescriptionPrefix', 'No agent with ID')} \"${id}\" ${tr('agentDetails.notFoundDescriptionSuffix', 'exists')}`}\n action={\n <Button type=\"button\" onClick={handleBack}>\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </Button>\n }\n />\n </MotionPage>\n );\n }\n\n const agentTabs: { id: AgentTab; label: string; icon: typeof Server; badge?: string }[] = [\n { id: 'overview', label: tr('nav.overview', 'Overview'), icon: Server },\n { id: 'profiles', label: tr('agentDetails.profiles', 'Profiles'), icon: Users },\n { id: 'security', label: tr('agentDetails.security', 'Security'), icon: Shield },\n {\n id: 'plugins',\n label: tr('agentDetails.plugins', 'Plugins'),\n icon: Puzzle,\n badge: `${installedPlugins.length}`,\n },\n {\n id: 'sessions',\n label: tr('nav.sessions', 'Sessions'),\n icon: Terminal,\n badge: `${agentSessions.length}`,\n },\n {\n id: 'logs',\n label: tr('nav.logs', 'Logs'),\n icon: ScrollText,\n },\n {\n id: 'connection',\n label: tr('agentDetails.connection', 'Connection Log'),\n icon: ScrollText,\n },\n {\n id: 'vibes',\n label: tr('nav.vibes', 'Vibes'),\n icon: Zap,\n badge: `${agentVibes.length}`,\n },\n {\n id: 'notes',\n label: tr('vibeDetails.notes', 'Notes'),\n icon: FileText,\n },\n {\n id: 'aliases',\n label: tr('agentDetails.aliases', 'Aliases'),\n icon: Command,\n },\n {\n id: 'backup',\n label: tr('agentDetails.backup', 'Backup'),\n icon: Database,\n },\n {\n id: 'maintenance',\n label: tr('agentDetails.maintenance.tab', 'Maintenance'),\n icon: Wrench,\n },\n ];\n\n // Types for sessions and vibes from the query\n type SessionItem = NonNullable<typeof agentSessions>[number];\n type VibeItem = NonNullable<typeof agentVibes>[number];\n\n // Stats\n const runningSessions = agentSessions.filter((s: SessionItem) => s.status === 'RUNNING').length;\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('agentDetails.backToAgents', 'Back to Agents')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={agent.name}\n description={\n agent.hostname || tr('agentDetails.noHostnameConfigured', 'No hostname configured')\n }\n breadcrumb={[\n { label: tr('nav.agents', 'Agents'), path: basePath ? `${basePath}/agents` : '/agents' },\n { label: agent.name },\n ]}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n {!showStartButton ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleStopAgent}\n disabled={lifecycleAction !== null}\n aria-busy={lifecycleAction === 'stop'}\n >\n {lifecycleAction === 'stop' ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Square className=\"size-4\" aria-hidden=\"true\" />\n )}\n {lifecycleAction === 'stop'\n ? tr('agentDetails.stopping', 'Stopping…')\n : tr('sessions.stop', 'Stop')}\n </Button>\n ) : (\n <Button\n type=\"button\"\n onClick={handleStartAgent}\n disabled={lifecycleAction !== null}\n aria-busy={lifecycleAction === 'start'}\n >\n {lifecycleAction === 'start' ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Play className=\"size-4\" aria-hidden=\"true\" />\n )}\n {lifecycleAction === 'start'\n ? tr('agentDetails.starting', 'Starting…')\n : tr('agentDetails.start', 'Start')}\n </Button>\n )}\n {derivedStatus === 'DEGRADED' && (\n // BOFF-2628 — Reconnect on Degraded. Fires the same lifecycle\n // start as a fresh start; svc-side that POSTs lifecycle/start\n // to the agent's tunnel, which typically clears transient\n // failures (tunnel hiccup, finalize retry). The button is\n // labelled \"Reconnect\" so the user knows it's a recovery\n // action — not a fresh start that might collide.\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={handleStartAgent}\n disabled={lifecycleAction !== null}\n aria-busy={lifecycleAction === 'start'}\n className=\"text-status-warning-text hover:text-status-warning-text\"\n title={\n probesById.get(agent.id)?.errorMessage\n ? `Retry lifecycle/start — last error: ${probesById.get(agent.id)?.errorMessage}`\n : 'Retry lifecycle/start'\n }\n >\n {lifecycleAction === 'start' ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <RefreshCw className=\"size-4\" aria-hidden=\"true\" />\n )}\n {lifecycleAction === 'start'\n ? tr('agentDetails.reconnecting', 'Reconnecting…')\n : tr('agentDetails.reconnect', 'Reconnect')}\n </Button>\n )}\n {/* Self-contained control (probe → start → mint flow lives inside\n the component), so it stays a visible button rather than an\n ActionMenu item. */}\n <OpenInBrowserEditorButton\n agentId={agent.id}\n tunnelUrl={agent.tunnelUrl || null}\n profile={activeProfile}\n />\n <Button type=\"button\" variant=\"outline\" onClick={handleConfigureAgent}>\n <Settings className=\"size-4\" aria-hidden=\"true\" />\n {tr('agentDetails.configure', 'Configure')}\n </Button>\n <ActionMenu\n ariaLabel={tr('agentDetails.agentActions', 'Agent actions')}\n title={tr('agentDetails.agentActions', 'Agent actions')}\n align=\"end\"\n items={[\n {\n key: 'ai-chat',\n label: tr('agentDetails.aiChat', 'AI Chat'),\n icon: Bot,\n onSelect: () => navigate('/ai?contextLevel=agent'),\n },\n {\n key: 'refresh',\n label: tr('common.refresh', 'Refresh'),\n icon: RefreshCw,\n onSelect: () => void refetch(),\n disabled: loading,\n },\n // Only surface Remove to actors with agent:delete — the\n // backend @rbac(action:\"delete\") 403s otherwise, so hiding\n // the option keeps the UI honest (BOFF-2688).\n ...(hasPermission(VIBE_PERMISSIONS.AGENT_DELETE)\n ? [\n {\n key: 'remove',\n label: tr('sessions.remove', 'Remove'),\n icon: Trash2,\n onSelect: handleRemoveAgent,\n variant: 'danger' as const,\n separatorBefore: true,\n },\n ]\n : []),\n ]}\n />\n </div>\n }\n />\n\n <PagePurpose icon={<Server className=\"size-4\" />}>\n {tr(\n 'agentDetails.purpose',\n 'This page is the control panel for one agent — the program running on your machine that VibeControls uses to execute work. Here you check its health and connection, start or restart it, push gateway credentials, review its sessions and security, and copy the commands to (re)install it. Come here when an agent is degraded or you need to confirm it is reachable.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"vibecontrols-agent-details\"\n steps={[\n {\n id: 'start-session',\n label: tr('agentDetails.next.startSession', 'Start a session on this agent'),\n description: tr(\n 'agentDetails.next.startSessionDesc',\n 'Open a terminal or task that runs on this machine.'\n ),\n icon: <Plus className=\"size-4\" />,\n onClick: handleStartSession,\n },\n {\n id: 'check-connection',\n label: tr('agentDetails.next.checkConnection', 'Check the connection'),\n description: tr(\n 'agentDetails.next.checkConnectionDesc',\n 'Verify the tunnel URL and reachability on the Connection tab.'\n ),\n icon: <Server className=\"size-4\" />,\n onClick: () => setActiveTab('connection'),\n },\n {\n id: 'review-security',\n label: tr('agentDetails.next.reviewSecurity', 'Review security & credentials'),\n description: tr(\n 'agentDetails.next.reviewSecurityDesc',\n 'Confirm gateway auth is pushed and scopes are correct.'\n ),\n icon: <Bot className=\"size-4\" />,\n onClick: () => setActiveTab('security'),\n },\n ]}\n />\n\n {/* Inline rename row — quick edit for the agent's display name.\n The Configure modal still owns the full edit (tunnel URL,\n ai-context, metadata, etc.); this is the one-click affordance\n for the common case of \"I just want to rename this thing\". */}\n <div className=\"flex items-center gap-2 -mt-2\">\n {renaming ? (\n <>\n <input\n ref={renameInputRef}\n type=\"text\"\n value={renameValue}\n onChange={(e) => setRenameValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !renameSaving) {\n void handleRenameSave();\n } else if (e.key === 'Escape') {\n handleRenameCancel();\n }\n }}\n disabled={renameSaving}\n maxLength={120}\n aria-label={tr('agentDetails.renameInput', 'New agent name')}\n className=\"flex-1 max-w-md px-3 py-1.5 text-sm bg-bg-surface border border-border-subtle rounded-input text-text-primary placeholder:text-text-placeholder transition-colors focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\"\n placeholder={agent.name}\n data-testid=\"agent-details-rename-input\"\n />\n <button\n type=\"button\"\n onClick={() => void handleRenameSave()}\n disabled={!renameValue.trim() || renameValue.trim() === agent.name || renameSaving}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bgHover transition-colors disabled:opacity-60 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n data-testid=\"agent-details-rename-save\"\n >\n {renameSaving ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Check className=\"size-3.5\" />\n )}\n {tr('agentDetails.saveName', 'Save')}\n </button>\n <button\n type=\"button\"\n onClick={handleRenameCancel}\n disabled={renameSaving}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary disabled:opacity-60\"\n data-testid=\"agent-details-rename-cancel\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n {renameError && (\n <span className=\"text-xs text-status-error-text\" role=\"alert\">\n {renameError}\n </span>\n )}\n </>\n ) : (\n <button\n type=\"button\"\n onClick={() => {\n setRenameError(null);\n setRenameValue(agent.name);\n setRenaming(true);\n requestAnimationFrame(() => renameInputRef.current?.select());\n }}\n className=\"inline-flex items-center gap-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n title={tr('agentDetails.renameTooltip', 'Rename this agent')}\n data-testid=\"agent-details-rename-trigger\"\n >\n <Edit3 className=\"size-3.5\" />\n {tr('agentDetails.rename', 'Rename')}\n </button>\n )}\n </div>\n\n {/* Auto-reconnecting banner — shown only while the badge says DEGRADED.\n Pairs with the visible Reconnect button above so the user can\n either wait for the next health probe (every ~30s) to recover the\n agent automatically, or click Reconnect to retry now. The latest\n probe error message and the suggested action items are surfaced\n inline instead of hiding them behind a tooltip on the badge. */}\n {derivedStatus === 'DEGRADED' && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex items-start gap-3 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle px-4 py-3 text-sm text-text-primary\"\n >\n <Loader2\n className=\"size-4 mt-0.5 flex-shrink-0 animate-spin text-status-warning-text\"\n aria-hidden=\"true\"\n />\n <div className=\"flex-1 min-w-0 space-y-1\">\n <p className=\"font-medium\">\n {tr(\n 'agentDetails.degradedBanner.title',\n 'Agent is degraded — auto-reconnecting in the background'\n )}\n </p>\n {probesById.get(agent.id)?.errorMessage && (\n <p className=\"text-xs text-text-secondary\">\n <span className=\"font-mono text-status-warning-text\">\n {probesById.get(agent.id)?.errorMessage}\n </span>\n </p>\n )}\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'agentDetails.degradedBanner.action',\n 'Health probes run every ~30s; the badge flips back to Active automatically once a probe succeeds. You can click Reconnect above to retry now, or check the Connection tab for the probe history.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => setActiveTab('connection')}\n className=\"text-xs font-medium text-text-link hover:underline flex-shrink-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus rounded\"\n >\n {tr('agentDetails.degradedBanner.viewLog', 'View log →')}\n </button>\n </div>\n )}\n\n {/* Status Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Server className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">{tr('sessions.filterStatus', 'Status')}</p>\n <AgentStatusBadge\n status={derivedStatus}\n reason={\n probesById.get(agent.id)?.errorMessage ??\n (agent.lastGatewayAuthPushStatus === 'failed'\n ? (agent.lastGatewayAuthPushError ?? 'Gateway-auth push failed')\n : null)\n }\n linkTo={`?tab=connection`}\n onClick={(event) => {\n event.preventDefault();\n setActiveTab('connection');\n }}\n />\n </div>\n </div>\n </Card>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Cpu className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">{tr('analytics.platform', 'Platform')}</p>\n <p className=\"font-semibold text-text-primary\">\n {agent.platform}/{agent.architecture}\n </p>\n </div>\n </div>\n </Card>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <HardDrive className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">{tr('agentDetails.version', 'Version')}</p>\n <p className=\"font-semibold text-text-primary\">v{agent.version}</p>\n </div>\n </div>\n </Card>\n <Card className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Terminal className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">{tr('nav.sessions', 'Sessions')}</p>\n <p className=\"font-semibold text-text-primary\">\n {runningSessions} {tr('health.active', 'active')}\n </p>\n </div>\n </div>\n </Card>\n </div>\n\n {/* ── Tab Navigation Bar ─────────────────────────────────────────── */}\n {/* fe-libs chrome Tabs supplies tablist/tab roles + arrow-key nav.\n Static tabs and PR-8 iframe-mounted plugin tabs (feature-flagged,\n same gate as VibeDetailsPage) merge into one strip; \"+ Add tab\"\n stays a separate trailing button since it opens a dialog rather\n than activating a panel. */}\n <div className=\"flex items-end border-b border-border-default\">\n <Tabs\n items={[\n ...agentTabs.map(({ id: tabId, label, icon: TabIcon, badge }): TabItem => ({\n id: tabId,\n label,\n icon: <TabIcon className=\"size-4\" aria-hidden=\"true\" />,\n badge,\n })),\n ...(enableIframeHost\n ? iframeContributions.data.map((c): TabItem => ({\n id: c.contribution.id,\n label: c.contribution.title,\n }))\n : []),\n ]}\n activeTabId={activeTab}\n onTabChange={setActiveTab}\n variant=\"underline\"\n className=\"min-w-0 flex-1 border-b-0\"\n />\n {/* \"+ Add tab\" — placed at the trailing end of the strip. */}\n {enableIframeHost && agent && (\n <button\n type=\"button\"\n onClick={() => setAddTabOpen(true)}\n className=\"-mb-px flex shrink-0 items-center gap-1 p-2 text-xs sm:text-sm border-b-2 border-transparent text-text-secondary hover:text-text-primary hover:border-border-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus rounded-t\"\n title=\"Install a UI plugin tab\"\n aria-label=\"Add tab\"\n >\n <span aria-hidden>+</span>\n <span className=\"hidden sm:inline\">Add tab</span>\n </button>\n )}\n </div>\n\n {/* ── Tab Panels ─────────────────────────────────────────────────── */}\n <MotionTabPanel tabKey={activeTab}>\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"space-y-6\">\n {/* Capabilities */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <CheckCircle2 className=\"size-5\" />\n {tr('agentDetails.capabilities', 'Capabilities')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"flex flex-wrap gap-2\">\n {agent.capabilities &&\n typeof agent.capabilities === 'object' &&\n (Array.isArray(agent.capabilities)\n ? agent.capabilities.length > 0\n : Object.keys(agent.capabilities).length > 0) ? (\n (Array.isArray(agent.capabilities)\n ? (agent.capabilities as string[])\n : (() => {\n const obj = agent.capabilities as Record<string, unknown>;\n const keys = Object.keys(obj);\n const hasNumericKeys =\n keys.length > 0 && keys.every((k) => /^\\d+$/.test(k));\n return hasNumericKeys\n ? (Object.values(obj) as string[])\n : (keys as string[]);\n })()\n ).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 bg-bg-sunken text-text-secondary text-xs rounded-pill\"\n >\n {cap}\n </span>\n ))\n ) : (\n <div className=\"space-y-2 text-sm text-text-secondary\">\n <p>\n {tr('agentDetails.noCapabilitiesConfigured', 'No capabilities configured')}\n </p>\n <p className=\"text-xs\">\n Capabilities are feature tags the agent advertises to vibes (e.g.{' '}\n <code className=\"text-text-primary\">docker</code>,{' '}\n <code className=\"text-text-primary\">gpu</code>,{' '}\n <code className=\"text-text-primary\">node20</code>,{' '}\n <code className=\"text-text-primary\">git</code>). Vibes use them to pick an\n agent that can run their workloads.\n </p>\n <p className=\"text-xs\">\n Add from the agent host CLI:{' '}\n <code className=\"text-text-primary\">vibe agent caps add docker gpu</code>.\n The agent re-publishes its capability set on next heartbeat.\n </p>\n </div>\n )}\n </div>\n </div>\n </Card>\n\n {/* Tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Tag className=\"size-5\" />\n Tags\n </h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_AGENT\" entityId={agent.id} />\n </div>\n </Card>\n\n {/* Tunnel Info */}\n {agent.tunnelUrl && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Zap className=\"size-5\" />\n {tr('health.tunnel', 'Tunnel')}\n </h3>\n </div>\n <div className=\"p-4\">\n {/*\n * Tunnel URL is a plain info field — no status chip\n * here. The agent's canonical status lives in the\n * top \"Status\" card above; rendering tunnelStatus\n * here was a redundant (and frequently stale)\n * second status surface.\n */}\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm text-text-secondary mb-1\">\n {tr('agentDetails.tunnelUrl', 'Tunnel URL')}\n </p>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-text-link hover:underline break-all\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n </div>\n </Card>\n )}\n\n {/* Configuration */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Settings className=\"size-5\" />\n {tr('agentDetails.configuration', 'Configuration')}\n </h3>\n </div>\n <div className=\"p-4 space-y-4\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('analytics.platform', 'Platform')}\n </p>\n <p className=\"font-medium text-text-primary\">{agent.platform}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('analytics.architecture', 'Architecture')}\n </p>\n <p className=\"font-medium text-text-primary\">{agent.architecture}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {tr('agentDetails.version', 'Version')}\n </p>\n <p className=\"font-medium text-text-primary\">{agent.version}</p>\n </div>\n </div>\n {agent.aiContext && (\n <div>\n <p className=\"text-sm text-text-secondary mb-1\">AI Context</p>\n <pre className=\"text-sm text-text-primary bg-bg-sunken border border-border-default rounded-md p-3 whitespace-pre-wrap font-sans\">\n {agent.aiContext}\n </pre>\n </div>\n )}\n </div>\n </Card>\n\n {/* Timeline */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Clock className=\"size-5\" />\n {tr('vibeDetails.timeline', 'Timeline')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">\n {tr('health.lastHeartbeat', 'Last Heartbeat')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(agent.lastHeartbeat)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('vibeDetails.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(agent.createdAt)}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('vibeDetails.updated', 'Updated')}</p>\n <p className=\"font-medium text-text-primary\">\n {formatTimestamp(agent.updatedAt)}\n </p>\n </div>\n </div>\n </div>\n </Card>\n </div>\n )}\n\n {/* Profiles Tab — Phase 5B (multi-profile-per-daemon) */}\n {activeTab === 'profiles' && <ProfilesPanel agentId={agent.id} />}\n\n {/* Security Tab */}\n {activeTab === 'security' && (\n <div className=\"space-y-6\">\n {/* Agent API Key */}\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Key className=\"size-5\" />\n {tr('agentDetails.agentApiKey', 'Agent API Key')}\n </h3>\n </div>\n <div className=\"p-4\">\n {agent.tunnelUrl ? (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex-1 min-w-0\">\n <code className=\"text-sm font-mono text-text-primary bg-bg-sunken px-3 py-2 rounded-md block truncate\">\n {showApiKey && revealedApiKey\n ? revealedApiKey\n : revealLoading\n ? tr('agentDetails.revealLoading', 'Revealing\\u2026')\n : '\\u2022'.repeat(32)}\n </code>\n {revealError && (\n <p className=\"text-xs text-status-error-text mt-1\">{revealError}</p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => void handleToggleShowApiKey()}\n disabled={revealLoading}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors flex-shrink-0 disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n aria-label={\n showApiKey\n ? tr('agentDetails.hideApiKey', 'Hide API key')\n : tr('agentDetails.showApiKey', 'Show API key')\n }\n title={\n showApiKey\n ? tr('agentDetails.hideApiKey', 'Hide API key')\n : tr('agentDetails.showApiKey', 'Show API key')\n }\n >\n {showApiKey ? (\n <EyeOff className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n ) : (\n <Eye className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n )}\n </button>\n <button\n type=\"button\"\n onClick={handleCopyApiKey}\n className=\"p-2 rounded hover:bg-bg-sunken transition-colors flex-shrink-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n aria-label={tr('agentDetails.copyApiKey', 'Copy API key')}\n title={tr('agentDetails.copyApiKey', 'Copy API key')}\n >\n {apiKeyCopied ? (\n <Check className=\"size-4 text-status-success-text\" aria-hidden=\"true\" />\n ) : (\n <Copy className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n )}\n </button>\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Agent tunnel not configured. Bring the agent online first.\n </p>\n )}\n <p className=\"mt-2 text-xs text-text-muted\">\n This key authenticates requests sent to the agent. The key is revealed only on\n explicit operator action and every read is audited.\n </p>\n </div>\n </Card>\n\n {/* Emergency connection recovery — set API key (from `vibe key`)\n or update the tunnel URL in place when auto-report is broken,\n without delete + re-add. */}\n <AgentConnectionRecovery\n agentId={agent.id}\n currentTunnelUrl={agent.tunnelUrl}\n onUpdated={() => refetch()}\n />\n\n {/* Reuse an existing database for this agent. */}\n <AgentStorageConfig\n agentId={agent.id}\n storageAdapter={agent.storageAdapter}\n dbPath={agent.dbPath}\n hasStorageOptions={agent.hasStorageOptions}\n onUpdated={() => refetch()}\n />\n\n {/* Gateway Auth (OAuth2 App) */}\n <GatewayAuthCard agent={agent} onUpdate={() => refetch()} />\n\n {/* Remote Task Permissions — opt-in toggles for the\n \"command/script over tunnel\" + \"file write/delete over tunnel\"\n gates that block VibeDeck buttons / scheduled jobs by default. */}\n <RemoteTaskPermissionsCard\n agent={{ id: agent.id, profile: activeProfile ?? 'default' }}\n />\n </div>\n )}\n\n {/* Plugins Tab */}\n {activeTab === 'plugins' &&\n (() => {\n const searchLower = pluginSearchQuery.toLowerCase();\n const filteredInstalled = installedPlugins.filter(\n (p) =>\n !searchLower ||\n p.pluginName.toLowerCase().includes(searchLower) ||\n p.packageName.toLowerCase().includes(searchLower) ||\n p.description?.toLowerCase().includes(searchLower) ||\n p.tags?.some((t) => t.toLowerCase().includes(searchLower))\n );\n const filteredAvailable = availablePlugins.filter(\n (p) =>\n !p.installed &&\n (!searchLower ||\n p.name.toLowerCase().includes(searchLower) ||\n p.packageName.toLowerCase().includes(searchLower) ||\n p.description.toLowerCase().includes(searchLower))\n );\n const uiPlugins = installedPlugins.filter((p) => p.hasUI);\n const filteredUIPlugins = uiPlugins.filter(\n (p) =>\n !searchLower ||\n p.pluginName.toLowerCase().includes(searchLower) ||\n p.packageName.toLowerCase().includes(searchLower)\n );\n\n const pluginSubTabs: {\n id: PluginSubTab;\n label: string;\n count: number;\n icon: typeof Puzzle;\n }[] = [\n {\n id: 'installed',\n label: 'Installed',\n count: filteredInstalled.length,\n icon: Puzzle,\n },\n {\n id: 'available',\n label: 'Available',\n count: filteredAvailable.length,\n icon: Download,\n },\n {\n id: 'store',\n label: 'Store',\n count: storePlugins.length,\n icon: Download,\n },\n {\n id: 'ui-plugins',\n label: 'UI Plugins',\n count: filteredUIPlugins.length,\n icon: Monitor,\n },\n ];\n\n return (\n <div className=\"space-y-6\">\n <Card>\n {/* Header with title, search, and refresh */}\n <div className=\"p-4 border-b border-border-default space-y-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2 min-w-0\">\n <Puzzle className=\"size-5\" aria-hidden=\"true\" />\n Plugins\n {installedPlugins.length > 0 && (\n <span className=\"text-sm font-normal text-text-secondary\">\n ({installedPlugins.filter((p) => p.isCore).length} core,{' '}\n {installedPlugins.filter((p) => !p.isCore).length} external)\n </span>\n )}\n </h3>\n <button\n type=\"button\"\n onClick={fetchPlugins}\n disabled={pluginsLoading}\n className=\"p-1.5 text-text-secondary hover:bg-bg-sunken rounded transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n aria-label={tr('agentDetails.refreshPlugins', 'Refresh plugins')}\n title={tr('agentDetails.refreshPlugins', 'Refresh plugins')}\n >\n <RefreshCw\n className={`size-4 ${pluginsLoading ? 'animate-spin' : ''}`}\n aria-hidden=\"true\"\n />\n </button>\n </div>\n\n {/* Search bar */}\n <SearchInput\n value={pluginSearchQuery}\n onChange={setPluginSearchQuery}\n placeholder={tr(\n 'agentDetails.searchPluginsPlaceholder',\n 'Search plugins by name, package, description, or tag...'\n )}\n />\n\n {/* Sub-tabs */}\n <Tabs\n items={pluginSubTabs.map(\n ({ id: subTabId, label, count, icon: SubTabIcon }): TabItem => ({\n id: subTabId,\n label,\n icon: <SubTabIcon className=\"size-4\" aria-hidden=\"true\" />,\n badge: count,\n })\n )}\n activeTabId={pluginSubTab}\n onTabChange={(subTabId) => setPluginSubTab(subTabId as PluginSubTab)}\n variant=\"pills\"\n size=\"sm\"\n className=\"max-w-full overflow-x-auto\"\n />\n </div>\n\n <div className=\"p-4 space-y-4\">\n {/* Plugin Error */}\n {pluginError && (\n <div\n role=\"alert\"\n className=\"flex items-center gap-2 p-3 bg-status-error-bg-subtle border border-status-error-border text-status-error-text rounded-md text-sm\"\n >\n <X className=\"size-4 flex-shrink-0\" aria-hidden=\"true\" />\n <span className=\"flex-1 min-w-0 break-words\">{pluginError}</span>\n <button\n type=\"button\"\n onClick={() => setPluginError(null)}\n aria-label={tr('agentDetails.dismissError', 'Dismiss error')}\n className=\"flex-shrink-0 p-0.5 hover:bg-status-error-border/40 rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n </div>\n )}\n\n {/* Not reachable banner */}\n {!agent?.isActive && (\n <p\n role=\"status\"\n className=\"text-sm text-text-muted flex items-center gap-1.5\"\n >\n <Info className=\"size-4 flex-shrink-0\" aria-hidden=\"true\" />\n Agent is offline — showing known plugin catalog. Live data will load when\n the agent is reachable.\n </p>\n )}\n\n {/* Plugins are agent-wide (BOFF-2612). No per-profile\n gating: a single install/remove action applies to the\n whole agent. Only plugin DATA stays per-profile. */}\n\n {/* ── Installed Sub-Tab ────────────────────────────────── */}\n {pluginSubTab === 'installed' && (\n <>\n {filteredInstalled.length > 0 ? (\n <div className=\"space-y-2\">\n {filteredInstalled.map((plugin) => (\n <div\n key={plugin.packageName}\n className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2 p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary flex items-center gap-1.5 flex-wrap\">\n {plugin.pluginName}\n <span className=\"text-xs text-text-muted font-normal\">\n {plugin.version === 'latest'\n ? plugin.version\n : `v${plugin.version}`}\n </span>\n {plugin.isCore && (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n core\n </span>\n )}\n {plugin.loaded && (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs bg-status-success-bg-subtle text-status-success-text rounded\">\n loaded\n </span>\n )}\n {plugin.hasProviders && (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n provider\n </span>\n )}\n {plugin.hasUI && (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n ui\n </span>\n )}\n </p>\n {plugin.description && (\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {plugin.description}\n </p>\n )}\n <div className=\"flex items-center gap-2 mt-0.5 flex-wrap\">\n {!plugin.isCore && (\n <span className=\"text-xs text-text-muted font-mono\">\n {plugin.packageName}\n </span>\n )}\n {plugin.cliCommand && (\n <span className=\"text-xs text-text-muted\">\n cli:{' '}\n <code className=\"text-xs font-mono\">\n vibe {plugin.cliCommand}\n </code>\n </span>\n )}\n {plugin.apiPrefix && (\n <span className=\"text-xs text-text-muted\">\n api:{' '}\n <code className=\"text-xs font-mono\">\n {plugin.apiPrefix}\n </code>\n </span>\n )}\n </div>\n {plugin.tags && plugin.tags.length > 0 && (\n <div className=\"flex gap-1 mt-1 flex-wrap\">\n {plugin.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex px-1.5 py-0.5 text-xs bg-bg-sunken text-text-muted rounded\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2 flex-shrink-0 sm:ml-3\">\n {plugin.hasUI && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPluginSubTab('ui-plugins')}\n title={`Open ${plugin.pluginName} UI`}\n >\n <Monitor className=\"size-3\" aria-hidden=\"true\" />\n Open UI\n </Button>\n )}\n {!plugin.isCore && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleUpdatePlugin(plugin.packageName)}\n disabled={pluginActionLoading === plugin.packageName}\n title={tr(\n 'agentDetails.updatePlugin',\n 'Update plugin (latest or a specific version)'\n )}\n >\n {pluginActionLoading === plugin.packageName ? (\n <Loader2\n className=\"size-3 animate-spin\"\n aria-hidden=\"true\"\n />\n ) : (\n <ArrowUpCircle className=\"size-3\" aria-hidden=\"true\" />\n )}\n {tr('agentDetails.update', 'Update')}\n </Button>\n )}\n {!plugin.isCore && (\n // removeAgentPlugin is @rbac(action:\"update\",\n // resource:\"agent\") — hide the destructive\n // Remove for actors without agent:update so\n // the backend 403 isn't surfaced (BOFF-2688).\n <PermissionButton\n permission={VIBE_PERMISSIONS.AGENT_UPDATE}\n deniedBehavior=\"hide\"\n >\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleRemovePlugin(plugin.packageName)}\n disabled={pluginActionLoading === plugin.packageName}\n className=\"text-status-error-text hover:bg-status-error-bg-subtle hover:text-status-error-text\"\n >\n {pluginActionLoading === plugin.packageName ? (\n <Loader2\n className=\"size-3 animate-spin\"\n aria-hidden=\"true\"\n />\n ) : (\n <Trash2 className=\"size-3\" aria-hidden=\"true\" />\n )}\n {tr('sessions.remove', 'Remove')}\n </Button>\n </PermissionButton>\n )}\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-muted\">\n {pluginSearchQuery\n ? 'No installed plugins match your search.'\n : 'No plugin data available. Check agent connectivity or refresh.'}\n </p>\n )}\n </>\n )}\n\n {/* ── Available Sub-Tab ────────────────────────────────── */}\n {pluginSubTab === 'available' && (\n <>\n {filteredAvailable.length > 0 ? (\n <div className=\"space-y-2\">\n {filteredAvailable.map((plugin) => (\n <div\n key={plugin.packageName}\n className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2 p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {plugin.name}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {plugin.description}\n </p>\n <p className=\"text-xs text-text-muted font-mono mt-0.5\">\n {plugin.packageName}\n </p>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => handleInstallPlugin(plugin.packageName)}\n disabled={pluginActionLoading === plugin.packageName}\n className=\"flex-shrink-0 sm:ml-3\"\n >\n {pluginActionLoading === plugin.packageName ? (\n <Loader2 className=\"size-3 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <Download className=\"size-3\" aria-hidden=\"true\" />\n )}\n Install\n </Button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-muted\">\n {pluginSearchQuery\n ? 'No available plugins match your search.'\n : availablePlugins.every((p) => p.installed)\n ? 'All available plugins are already installed.'\n : 'No available plugins found.'}\n </p>\n )}\n </>\n )}\n\n {/* ── Store Sub-Tab (Burdenoff Store purchased plugins) ── */}\n {pluginSubTab === 'store' && (\n <>\n {storePluginsError && (\n <div\n role=\"alert\"\n className=\"flex items-center gap-2 p-3 bg-status-error-bg-subtle border border-status-error-border text-status-error-text rounded-md text-sm\"\n >\n <X className=\"size-4 flex-shrink-0\" aria-hidden=\"true\" />\n <span className=\"flex-1 min-w-0 break-words\">{storePluginsError}</span>\n </div>\n )}\n {storePluginsLoading ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Loading purchased plugins…\n </p>\n ) : storePlugins.length === 0 ? (\n <div className=\"py-8 text-center\">\n <p className=\"text-sm text-text-muted\">\n No plugins purchased from the Burdenoff store yet.\n </p>\n <a\n href=\"/store/marketplace\"\n className=\"mt-2 inline-flex items-center gap-1 text-xs font-semibold text-text-link hover:underline\"\n >\n Browse the store →\n </a>\n </div>\n ) : (\n <div className=\"space-y-2\">\n {storePlugins.map((ent) => {\n const manifest =\n (ent.product?.manifest as Record<string, unknown> | null) ?? {};\n const digital = manifest['digital'] as\n Record<string, unknown> | undefined;\n const install = digital?.['install'] as\n Record<string, unknown> | undefined;\n const packageName =\n typeof install?.['packageName'] === 'string'\n ? (install['packageName'] as string)\n : null;\n const isAlreadyInstalled = installedPlugins.some(\n (p) => p.packageName === packageName\n );\n return (\n <div\n key={ent.id}\n className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2 p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n {ent.product?.icon ? (\n <img\n src={ent.product.icon}\n alt=\"\"\n className=\"size-10 rounded-md object-cover flex-shrink-0\"\n />\n ) : (\n <div className=\"size-10 flex-shrink-0 rounded-md bg-bg-sunken flex items-center justify-center\">\n <Puzzle\n className=\"size-5 text-text-muted\"\n aria-hidden=\"true\"\n />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h4 className=\"text-sm font-semibold text-text-primary truncate\">\n {ent.product?.name ?? 'Unknown'}\n </h4>\n {ent.licenseKey && (\n <span className=\"text-xs bg-status-success-bg-subtle text-status-success-text px-1.5 py-0.5 rounded-full\">\n Licensed\n </span>\n )}\n </div>\n {packageName && (\n <p className=\"text-xs text-text-muted font-mono truncate\">\n {packageName}\n </p>\n )}\n {ent.product?.description && (\n <p className=\"text-xs text-text-secondary line-clamp-2 mt-1\">\n {ent.product.description}\n </p>\n )}\n </div>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() =>\n void handleInstallStorePlugin(ent.productId, ent.licenseKey)\n }\n disabled={\n isAlreadyInstalled ||\n storeInstallingId === ent.productId ||\n !packageName\n }\n aria-busy={storeInstallingId === ent.productId}\n className=\"flex-shrink-0\"\n >\n {isAlreadyInstalled\n ? 'Installed'\n : storeInstallingId === ent.productId\n ? 'Installing…'\n : 'Install'}\n </Button>\n </div>\n );\n })}\n </div>\n )}\n </>\n )}\n\n {/* ── UI Plugins Sub-Tab (Embedded Browser) ────────────── */}\n {pluginSubTab === 'ui-plugins' && (\n <>\n {uiPlugins.length > 0 || agent?.tunnelUrl ? (\n <PluginUIPanel\n availablePlugins={[\n // Synthetic code-server entry (proxied, not a standard UI plugin)\n ...(agent?.tunnelUrl\n ? [\n {\n pluginName: 'code-server',\n displayName: 'Code Server (VS Code)',\n description: 'Browser-based VS Code editor',\n version: 'latest',\n customUrl: '/code-server/',\n } satisfies UIPluginOption,\n ]\n : []),\n ...uiPlugins.map((p): UIPluginOption => ({\n pluginName: p.pluginName,\n displayName: p.pluginName\n .replace(/^ui-/, '')\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, (c) => c.toUpperCase()),\n description: p.description,\n version: p.version,\n })),\n ]}\n tunnelUrl={agent?.tunnelUrl || null}\n agentId={agent?.id ?? null}\n profile={activeProfile}\n />\n ) : (\n <p className=\"text-sm text-text-muted\">\n {pluginSearchQuery\n ? 'No UI plugins match your search.'\n : 'No plugins with UI are currently installed.'}\n </p>\n )}\n </>\n )}\n\n {/* Loading */}\n {pluginsLoading && installedPlugins.length === 0 && (\n <div\n role=\"status\"\n className=\"flex items-center gap-2 text-sm text-text-secondary\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n Loading plugins...\n </div>\n )}\n </div>\n </Card>\n </div>\n );\n })()}\n\n {/* Sessions Tab */}\n {activeTab === 'sessions' && (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between gap-3 flex-wrap\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"size-5\" />\n {tr('nav.sessions', 'Sessions')} ({agentSessions.length})\n </h3>\n <PermissionButton\n permission={VIBE_PERMISSIONS.SESSION_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip=\"You don't have permission to create sessions\"\n >\n <Button\n type=\"button\"\n size=\"sm\"\n data-testid=\"create-session-btn\"\n onClick={handleStartSession}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('sessions.createSession', 'Create Session')}\n </Button>\n </PermissionButton>\n </div>\n {agentSessions.length > 0 ? (\n <div className=\"divide-y divide-border-default\">\n {agentSessions.map((session: SessionItem) => (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleSessionClick(session.id);\n }\n }}\n key={session.id}\n onClick={() => handleSessionClick(session.id)}\n className=\"p-4 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 cursor-pointer hover:bg-bg-sunken/50 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-inset\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <Terminal\n className=\"size-5 text-text-secondary flex-shrink-0\"\n aria-hidden=\"true\"\n />\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{session.name}</p>\n <p className=\"text-sm text-text-secondary truncate\">{session.type}</p>\n </div>\n </div>\n <StatusBadge status={session.status} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"p-6 text-center text-text-secondary flex flex-col items-center gap-3\">\n <span>{tr('agentDetails.noSessionsOnAgent', 'No sessions on this agent')}</span>\n <PermissionButton\n permission={VIBE_PERMISSIONS.SESSION_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip=\"You don't have permission to create sessions\"\n >\n <Button\n type=\"button\"\n size=\"sm\"\n data-testid=\"create-session-empty-btn\"\n onClick={handleStartSession}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('sessions.createSession', 'Create Session')}\n </Button>\n </PermissionButton>\n </div>\n )}\n </Card>\n )}\n\n {activeTab === 'connection' && (\n <Card className=\"min-h-[60vh]\">\n <ConnectionLogPanel agentId={agent.id} agentName={agent.name} />\n </Card>\n )}\n\n {activeTab === 'logs' && (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <ScrollText className=\"size-5\" />\n {tr('nav.logs', 'Logs')}\n </h3>\n </div>\n <div className=\"p-4\">\n <LogViewer agentId={agent.id} profile={activeProfile} className=\"min-h-[28rem]\" />\n </div>\n </Card>\n )}\n\n {/* Vibes Tab */}\n {activeTab === 'vibes' && (\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Zap className=\"size-5\" />\n {tr('nav.vibes', 'Vibes')} ({agentVibes.length})\n </h3>\n </div>\n {agentVibes.length > 0 ? (\n <div className=\"divide-y divide-border-default\">\n {agentVibes.map((vibe: VibeItem) => (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleVibeClick(vibe.id);\n }\n }}\n key={vibe.id}\n onClick={() => handleVibeClick(vibe.id)}\n className=\"p-4 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 cursor-pointer hover:bg-bg-sunken/50 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-inset\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <Zap\n className=\"size-5 text-text-secondary flex-shrink-0\"\n aria-hidden=\"true\"\n />\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{vibe.name}</p>\n <p className=\"text-sm text-text-secondary truncate\">{vibe.type}</p>\n </div>\n </div>\n <StatusBadge status={vibe.status} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"p-6 text-center text-text-secondary\">\n {tr('agentDetails.noVibesUsingAgent', 'No vibes using this agent')}\n </div>\n )}\n </Card>\n )}\n\n {/* Notes Tab */}\n {activeTab === 'notes' && (\n <div className=\"space-y-6\">\n <NotesTabPanel\n context=\"agent\"\n agentVibes={agentVibes.map((v: VibeItem) => ({ id: v.id, name: v.name }))}\n />\n </div>\n )}\n\n {/* Aliases Tab */}\n {activeTab === 'aliases' && (\n <div className=\"space-y-6\">\n <AliasManager\n agent={{\n id: agent.id,\n profile:\n ((agent.metadata as Record<string, unknown> | null)?.profile as\n string | undefined) ?? 'default',\n }}\n />\n </div>\n )}\n\n {activeTab === 'backup' && agent.tunnelUrl && (\n <AgentBackupTab agentId={agent.id} profile={activeProfile} />\n )}\n\n {activeTab === 'backup' && !agent.tunnelUrl && (\n <Card className=\"p-6 text-center text-text-secondary\">\n <Database className=\"size-8 mx-auto mb-2 text-text-muted\" aria-hidden=\"true\" />\n <p>\n {tr(\n 'agentDetails.backupRequiresTunnelAndApiKey',\n 'Agent tunnel URL and API key are required for backup management.'\n )}\n </p>\n <p className=\"text-sm mt-1\">\n {tr(\n 'agentDetails.configureConnectionInOverview',\n 'Configure the agent connection in the Overview tab.'\n )}\n </p>\n </Card>\n )}\n\n {activeTab === 'maintenance' && (\n <AgentMaintenanceTab\n agentId={agent.id}\n agentVersion={agent.version}\n onAfterDestructiveNuke={handleAfterDestructiveNuke}\n />\n )}\n {/* PR-8 iframe-mounted tab body. Mirrors the VibeDetailsPage\n implementation; both pages currently use the same helper\n component (inline in VibeDetailsPage), but for now we\n duplicate the mount logic here for clarity. PR-11+ unifies\n via `useMountTabStrip`. */}\n {enableIframeHost &&\n (() => {\n const active: DiscoveredContribution | undefined = iframeContributions.data.find(\n (c) => c.contribution.id === activeTab\n );\n if (!active || !agent) return null;\n return (\n <div className=\"space-y-6\">\n <Card>\n <div className=\"p-0 h-[70vh] max-h-[40rem] min-h-[20rem]\">\n <PluginIframeMount\n contribution={active}\n tunnel={active.tunnel}\n restPaths={\n active.capabilities.restPaths.length > 0\n ? active.capabilities.restPaths\n : [`/ui/${active.pluginKey}`]\n }\n context={{ agentId: agent.id }}\n agentId={agent.id}\n profile={active.tunnel.profile}\n theme={{ colorMode: 'light', tokens: {} }}\n locale=\"en\"\n featureFlags={{}}\n hostVersion=\"1.0.0\"\n />\n </div>\n </Card>\n </div>\n );\n })()}\n </MotionTabPanel>\n\n {/* PR-10 \"+ Add tab\" picker. */}\n {enableIframeHost && agent && (\n <AddTabDialog\n isOpen={addTabOpen}\n onClose={() => setAddTabOpen(false)}\n mountPoint=\"agent.detailTab\"\n agentId={agent.id}\n profile={activeProfile ?? 'default'}\n onInstalled={() => {\n void iframeContributions.refetch();\n }}\n />\n )}\n\n {/* Configure Agent Dialog (extracted — see components/agents) */}\n <ConfigureAgentDialog\n agent={{\n id: agent.id,\n name: agent.name,\n platform: agent.platform,\n architecture: agent.architecture,\n version: agent.version,\n tunnelUrl: agent.tunnelUrl,\n aiContext: agent.aiContext,\n metadata: agent.metadata as Record<string, unknown> | null,\n }}\n isOpen={showConfigModal}\n onClose={() => setShowConfigModal(false)}\n onSaved={() =>\n notify.success(\n tr('agentDetails.configurationUpdated', 'Agent configuration updated successfully')\n )\n }\n />\n\n {/* Create Session Dialog (extracted — see components/agents) */}\n <CreateSessionDialog\n agentId={agent.id}\n agentName={agent.name}\n probe={probesById.get(agent.id)}\n isOpen={showSessionModal}\n onClose={() => setShowSessionModal(false)}\n onCreated={() =>\n notify.success(tr('sessions.createdSuccess', 'Session created successfully'))\n }\n onQuotaExhausted={() => {\n setShowSessionModal(false);\n markQuotaHandledLocally();\n setQuotaKind('session');\n setShowQuotaExhausted(true);\n }}\n />\n\n {/* Remove-plugin confirmation (replaces window.confirm) */}\n <ConfirmDialog\n isOpen={pluginRemoveTarget !== null}\n onClose={() => setPluginRemoveTarget(null)}\n onConfirm={() => {\n const packageName = pluginRemoveTarget;\n setPluginRemoveTarget(null);\n if (packageName) void performRemovePlugin(packageName);\n }}\n title={tr('agentDetails.removePluginTitle', 'Remove plugin')}\n message={tr('agentDetails.removePluginConfirm', 'Remove plugin {{packageName}}?', {\n packageName: pluginRemoveTarget ?? '',\n })}\n confirmLabel={tr('sessions.remove', 'Remove')}\n variant=\"danger\"\n />\n\n {/* Update-plugin version prompt (replaces window.prompt).\n Blank = latest; Cancel aborts. */}\n <PromptDialog\n isOpen={pluginUpdateTarget !== null}\n onClose={() => setPluginUpdateTarget(null)}\n onSubmit={(value) => {\n const packageName = pluginUpdateTarget;\n setPluginUpdateTarget(null);\n if (packageName) void performUpdatePlugin(packageName, value || undefined);\n }}\n title={tr('agentDetails.updatePluginTitle', 'Update plugin')}\n description={tr(\n 'agentDetails.updatePluginPrompt',\n 'Update {{packageName}} to which version? Leave blank for latest.',\n { packageName: pluginUpdateTarget ?? '' }\n )}\n label={tr('agentDetails.updatePluginVersionLabel', 'Version')}\n placeholder={tr('agentDetails.updatePluginVersionPlaceholder', 'latest')}\n confirmLabel={tr('agentDetails.update', 'Update')}\n />\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title={getQuotaDialogContent(quotaKind).title}\n message={getQuotaDialogContent(quotaKind).message}\n />\n\n <ConfirmDialog\n isOpen={showRemoveDialog}\n onClose={() => {\n if (!removingAgent) setShowRemoveDialog(false);\n }}\n onConfirm={handleConfirmRemoveAgent}\n title={tr('agentDetails.removeAgentTitle', 'Remove agent')}\n message={tr('agentDetails.removeAgentMessage', 'Remove “{{name}}” from this workspace.', {\n name: agent.name,\n })}\n confirmLabel={tr('sessions.remove', 'Remove')}\n variant=\"danger\"\n isLoading={removingAgent}\n >\n <div className=\"space-y-2\">\n <label className=\"flex items-start gap-3 p-3 rounded-md bg-bg-sunken border border-border-default cursor-pointer\">\n <Checkbox\n checked={uninstallFromTarget}\n disabled={removingAgent}\n onCheckedChange={(c) => setUninstallFromTarget(c === true)}\n className=\"mt-0.5\"\n />\n <span className=\"min-w-0\">\n <span className=\"block text-sm font-medium text-text-primary\">\n {tr('agentDetails.uninstallFromTarget', 'Uninstall agent from the target')}\n </span>\n <span className=\"block text-xs text-text-secondary mt-0.5\">\n {tr(\n 'agentDetails.uninstallFromTargetDesc',\n 'Also wipe all agent state + VibeControls plugins and uninstall the agent from the host machine. This cannot be undone.'\n )}\n </span>\n </span>\n </label>\n <label className=\"flex items-start gap-3 p-3 rounded-md bg-bg-sunken border border-border-default cursor-pointer\">\n <Checkbox\n checked={deleteTargetWithAgent}\n disabled={removingAgent}\n onCheckedChange={(c) => setDeleteTargetWithAgent(c === true)}\n className=\"mt-0.5\"\n />\n <span className=\"min-w-0\">\n <span className=\"block text-sm font-medium text-text-primary\">\n {tr('agentDetails.deleteTarget', 'Delete the mapped target')}\n </span>\n <span className=\"block text-xs text-text-secondary mt-0.5\">\n {tr(\n 'agentDetails.deleteTargetDesc',\n 'Also delete the target this agent is mapped to. Any other agents on the same target are removed too. This cannot be undone.'\n )}\n </span>\n </span>\n </label>\n </div>\n </ConfirmDialog>\n </MotionPage>\n );\n}\n"],"mappings":"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"}