diff --git a/apps/desktop/src/renderer/hooks/host-service/useTerminalAgentBindings/useTerminalAgentBindings.ts b/apps/desktop/src/renderer/hooks/host-service/useTerminalAgentBindings/useTerminalAgentBindings.ts index bcfa737f376..04665c82ae0 100644 --- a/apps/desktop/src/renderer/hooks/host-service/useTerminalAgentBindings/useTerminalAgentBindings.ts +++ b/apps/desktop/src/renderer/hooks/host-service/useTerminalAgentBindings/useTerminalAgentBindings.ts @@ -18,6 +18,7 @@ export type TerminalAgentBinding = TerminalAgentBindings[number]; */ export function useTerminalAgentBindings( workspaceId: string, + options?: { enabled?: boolean }, ): Map { const hostUrl = useWorkspaceHostUrl(workspaceId); const queryClient = useQueryClient(); @@ -26,7 +27,8 @@ export function useTerminalAgentBindings( [hostUrl, workspaceId], ); - const enabled = Boolean(workspaceId) && Boolean(hostUrl); + const enabled = + (options?.enabled ?? true) && Boolean(workspaceId) && Boolean(hostUrl); const { data } = useQuery({ queryKey, diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx index 9f33462f1e0..cf5a3d45861 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx @@ -3,7 +3,6 @@ import { useDiffStats } from "renderer/hooks/host-service/useDiffStats"; import { useOptimisticCollectionActions } from "renderer/routes/_authenticated/hooks/useOptimisticCollectionActions"; import { useDeletingWorkspaces } from "renderer/routes/_authenticated/providers/DeletingWorkspacesProvider"; import { RenameBranchDialog } from "renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/components"; -import { useInlineWorkspacePortsEnabled } from "renderer/stores/inline-workspace-ports"; import { useV2WorkspaceNotificationStatus } from "renderer/stores/v2-notifications"; import { useDashboardSidebarHover } from "../../providers/DashboardSidebarHoverProvider"; import type { DashboardSidebarWorkspace } from "../../types"; @@ -43,7 +42,6 @@ export function DashboardSidebarWorkspaceItem({ const isMainWorkspace = workspace.type === "main"; const diffStats = useDiffStats(id); const workspaceStatus = useV2WorkspaceNotificationStatus(id); - const inlineWorkspacePortsEnabled = useInlineWorkspacePortsEnabled(); const { cancelRename, handleClearStatus, @@ -229,7 +227,7 @@ export function DashboardSidebarWorkspaceItem({ onSubmitRename={submitRename} onCancelRename={cancelRename} > - {!isPending && inlineWorkspacePortsEnabled && ( + {!isPending && ( + {agents.map((agent) => ( + + ))} + + ); +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx new file mode 100644 index 00000000000..8f3a1a039dc --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx @@ -0,0 +1,75 @@ +import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; +import { cn } from "@superset/ui/utils"; +import { useNavigate } from "@tanstack/react-router"; +import { LuBot } from "react-icons/lu"; +import { usePresetIcon } from "renderer/assets/app-icons/preset-icons"; +import { navigateToV2Workspace } from "renderer/routes/_authenticated/_dashboard/utils/workspace-navigation"; +import { + getStatusTooltip, + StatusIndicator, +} from "renderer/screens/main/components/StatusIndicator"; +import { STROKE_WIDTH } from "renderer/screens/main/components/WorkspaceSidebar/constants"; +import type { DashboardSidebarRunningAgent } from "../../hooks/useDashboardSidebarWorkspaceRunningAgents"; + +interface DashboardSidebarWorkspaceAgentBadgeProps { + workspaceId: string; + agent: DashboardSidebarRunningAgent; +} + +export function DashboardSidebarWorkspaceAgentBadge({ + workspaceId, + agent, +}: DashboardSidebarWorkspaceAgentBadgeProps) { + const navigate = useNavigate(); + const iconUrl = usePresetIcon(agent.agentId); + + const handleClick = () => { + void navigateToV2Workspace(workspaceId, navigate, { + search: { + terminalId: agent.terminalId, + focusRequestId: crypto.randomUUID(), + }, + }); + }; + + const statusLabel = + agent.status === "idle" ? "Idle" : getStatusTooltip(agent.status); + + return ( + + + + + +
+
{agent.label}
+
{statusLabel}
+
+ Click to open agent +
+
+
+
+ ); +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/index.ts new file mode 100644 index 00000000000..f8d19852bbb --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/index.ts @@ -0,0 +1 @@ +export { DashboardSidebarWorkspaceAgentBadge } from "./DashboardSidebarWorkspaceAgentBadge"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/index.ts new file mode 100644 index 00000000000..0b8cfe8562e --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/index.ts @@ -0,0 +1,5 @@ +export { + type DashboardSidebarRunningAgent, + type RunningAgentStatus, + useDashboardSidebarWorkspaceRunningAgents, +} from "./useDashboardSidebarWorkspaceRunningAgents"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/useDashboardSidebarWorkspaceRunningAgents.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/useDashboardSidebarWorkspaceRunningAgents.ts new file mode 100644 index 00000000000..f80a3884cea --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/useDashboardSidebarWorkspaceRunningAgents.ts @@ -0,0 +1,81 @@ +import { + BUILTIN_AGENT_LABELS, + type BuiltinAgentId, +} from "@superset/shared/agent-catalog"; +import { useMemo } from "react"; +import { useTerminalAgentBindings } from "renderer/hooks/host-service/useTerminalAgentBindings"; +import { + useV2NotificationStore, + type V2NotificationSource, +} from "renderer/stores/v2-notifications"; +import type { PaneStatus } from "shared/tabs-types"; +import { useShallow } from "zustand/react/shallow"; + +/** + * State of a bound agent. `idle` means the agent process is alive but not + * currently `working` / awaiting `permission` / ready for `review`. + */ +export type RunningAgentStatus = PaneStatus; + +export interface DashboardSidebarRunningAgent { + /** Stable key for React lists, derived from the notification source. */ + sourceKey: string; + source: V2NotificationSource; + /** Host terminal the agent is bound to. */ + terminalId: string; + /** Built-in agent id (`claude`, `codex`, …) — drives label + icon. */ + agentId: BuiltinAgentId; + /** `idle` | `working` | `permission` | `review`. */ + status: RunningAgentStatus; + /** When the agent process was bound (ms since epoch), used for stable order. */ + startedAt: number; + /** Agent display name (e.g. "Claude"). */ + label: string; +} + +/** + * Live list of agents bound to a workspace's terminals, newest binding last. + * Every live agent process is included regardless of state; its `status` comes + * from the notification store (or `idle` when it has no active status). + * + * Mirrors {@link useDashboardSidebarWorkspacePorts} so a workspace detail row + * can render agents the same way it renders ports. + */ +export function useDashboardSidebarWorkspaceRunningAgents( + workspaceId: string, + enabled = true, +): DashboardSidebarRunningAgent[] { + const bindings = useTerminalAgentBindings(workspaceId, { enabled }); + + const statusByTerminal = useV2NotificationStore( + useShallow((state) => { + const map: Record = {}; + for (const entry of Object.values(state.sources)) { + if ( + entry.workspaceId === workspaceId && + entry.source.type === "terminal" + ) { + map[entry.source.id] = entry.status; + } + } + return map; + }), + ); + + return useMemo(() => { + const agents: DashboardSidebarRunningAgent[] = []; + for (const binding of bindings.values()) { + agents.push({ + sourceKey: `terminal:${binding.terminalId}`, + source: { type: "terminal", id: binding.terminalId }, + terminalId: binding.terminalId, + agentId: binding.agentId, + status: statusByTerminal[binding.terminalId] ?? "idle", + startedAt: binding.startedAt, + label: BUILTIN_AGENT_LABELS[binding.agentId] ?? binding.agentId, + }); + } + agents.sort((a, b) => a.startedAt - b.startedAt); + return agents; + }, [bindings, statusByTerminal]); +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/index.ts new file mode 100644 index 00000000000..9f895f5c87c --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/index.ts @@ -0,0 +1,5 @@ +export { DashboardSidebarWorkspaceAgentsRow } from "./DashboardSidebarWorkspaceAgentsRow"; +export { + type DashboardSidebarRunningAgent, + useDashboardSidebarWorkspaceRunningAgents, +} from "./hooks/useDashboardSidebarWorkspaceRunningAgents"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx index 54e1043d238..bca10c28bb5 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx @@ -2,8 +2,12 @@ import { Fragment, type ReactNode } from "react"; import { LuX } from "react-icons/lu"; import { STROKE_WIDTH } from "renderer/screens/main/components/WorkspaceSidebar/constants"; import { useWorkspaceDetailsStore } from "renderer/stores"; +import { useInlineWorkspacePortsEnabled } from "renderer/stores/inline-workspace-ports"; +import { useWorkspaceAgentsRowEnabled } from "renderer/stores/workspace-agents-row"; import { useDashboardSidebarWorkspacePorts } from "../../../../providers/DashboardSidebarPortsProvider"; import { useDashboardSidebarPortKill } from "../../../DashboardSidebarPortsList/hooks/useDashboardSidebarPortKill"; +import { DashboardSidebarWorkspaceAgentsRow } from "../DashboardSidebarWorkspaceAgentsRow"; +import { useDashboardSidebarWorkspaceRunningAgents } from "../DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents"; import { DashboardSidebarWorkspacePortsRow } from "../DashboardSidebarWorkspacePortsRow"; import { DashboardSidebarWorkspaceDetailsAction } from "./components/DashboardSidebarWorkspaceDetailsAction"; import { DashboardSidebarWorkspaceDetailsToggle } from "./components/DashboardSidebarWorkspaceDetailsToggle"; @@ -45,13 +49,15 @@ export function DashboardSidebarWorkspaceDetails({ ); const { isPending: isKillingPorts, killPorts } = useDashboardSidebarPortKill(); + const inlineWorkspacePortsEnabled = useInlineWorkspacePortsEnabled(); + const workspaceAgentsRowEnabled = useWorkspaceAgentsRowEnabled(); // --- Section registry ----------------------------------------------------- const sections: WorkspaceDetailSection[] = []; const portGroup = useDashboardSidebarWorkspacePorts(workspaceId); const portCount = portGroup?.ports.length ?? 0; - if (portGroup && portCount > 0) { + if (inlineWorkspacePortsEnabled && portGroup && portCount > 0) { sections.push({ key: "ports", summary: `${portCount} ${portCount === 1 ? "port" : "ports"}`, @@ -72,7 +78,23 @@ export function DashboardSidebarWorkspaceDetails({ }); } - // Add more detail rows here (e.g. running agents). + const runningAgents = useDashboardSidebarWorkspaceRunningAgents( + workspaceId, + workspaceAgentsRowEnabled, + ); + const agentCount = runningAgents.length; + if (workspaceAgentsRowEnabled && agentCount > 0) { + sections.push({ + key: "agents", + summary: `${agentCount} ${agentCount === 1 ? "agent" : "agents"}`, + content: ( + + ), + }); + } // -------------------------------------------------------------------------- if (sections.length === 0) { diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspacePortsRow/DashboardSidebarWorkspacePortsRow.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspacePortsRow/DashboardSidebarWorkspacePortsRow.tsx index fa16737423c..0bf6a2bb17e 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspacePortsRow/DashboardSidebarWorkspacePortsRow.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspacePortsRow/DashboardSidebarWorkspacePortsRow.tsx @@ -1,4 +1,3 @@ -import { OverflowFadeContainer } from "@superset/ui/overflow-fade-container"; import { cn } from "@superset/ui/utils"; import { useDashboardSidebarWorkspacePorts } from "../../../../providers/DashboardSidebarPortsProvider"; import { DashboardSidebarPortBadge } from "../../../DashboardSidebarPortsList/components/DashboardSidebarPortBadge"; @@ -19,10 +18,9 @@ export function DashboardSidebarWorkspacePortsRow({ } return ( - @@ -32,6 +30,6 @@ export function DashboardSidebarWorkspacePortsRow({ port={port} /> ))} - + ); } diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx index 944589e2053..3ea8276d948 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/experimental/components/ExperimentalSettings/ExperimentalSettings.tsx @@ -12,6 +12,10 @@ import { } from "renderer/stores/inline-workspace-ports"; import { useOpenV1ImportModal } from "renderer/stores/v1-import-modal"; import { useV2LocalOverrideStore } from "renderer/stores/v2-local-override"; +import { + useWorkspaceAgentsRowEnabled, + useWorkspaceAgentsRowStore, +} from "renderer/stores/workspace-agents-row"; import { isItemVisible, SETTING_ITEM_ID, @@ -37,6 +41,10 @@ export function ExperimentalSettings({ SETTING_ITEM_ID.EXPERIMENTAL_INLINE_WORKSPACE_PORTS, visibleItems, ); + const showWorkspaceAgents = isItemVisible( + SETTING_ITEM_ID.EXPERIMENTAL_WORKSPACE_AGENTS, + visibleItems, + ); const isV2CloudEnabled = useIsV2CloudEnabled(); const isV2OnlyUser = useIsV2OnlyUser(); const setOptInV2 = useV2LocalOverrideStore((state) => state.setOptInV2); @@ -45,6 +53,10 @@ export function ExperimentalSettings({ const setInlineWorkspacePortsEnabled = useInlineWorkspacePortsStore( (state) => state.setEnabled, ); + const workspaceAgentsEnabled = useWorkspaceAgentsRowEnabled(); + const setWorkspaceAgentsEnabled = useWorkspaceAgentsRowStore( + (state) => state.setEnabled, + ); return (
@@ -126,6 +138,24 @@ export function ExperimentalSettings({ />
)} + {showWorkspaceAgents && ( +
+
+ +

+ Show running agents under each workspace in the sidebar, with + their live status. +

+
+ +
+ )} ); diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts index d54e3b6e29c..e620a78e36c 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts @@ -53,6 +53,7 @@ export const SETTING_ITEM_ID = { EXPERIMENTAL_SUPERSET_V2: "experimental-superset-v2", EXPERIMENTAL_V1_MIGRATION: "experimental-v1-migration", EXPERIMENTAL_INLINE_WORKSPACE_PORTS: "experimental-inline-workspace-ports", + EXPERIMENTAL_WORKSPACE_AGENTS: "experimental-workspace-agents", INTEGRATIONS_LINEAR: "integrations-linear", INTEGRATIONS_GITHUB: "integrations-github", @@ -164,6 +165,7 @@ export const SETTING_ITEM_VARIANT: Record = { [SETTING_ITEM_ID.EXPERIMENTAL_SUPERSET_V2]: "shared", [SETTING_ITEM_ID.EXPERIMENTAL_V1_MIGRATION]: "v2", [SETTING_ITEM_ID.EXPERIMENTAL_INLINE_WORKSPACE_PORTS]: "v2", + [SETTING_ITEM_ID.EXPERIMENTAL_WORKSPACE_AGENTS]: "v2", [SETTING_ITEM_ID.INTEGRATIONS_LINEAR]: "shared", [SETTING_ITEM_ID.INTEGRATIONS_GITHUB]: "shared", @@ -953,6 +955,26 @@ export const SETTINGS_ITEMS: SettingsItem[] = [ "switch", ], }, + { + id: SETTING_ITEM_ID.EXPERIMENTAL_WORKSPACE_AGENTS, + section: "experimental", + title: "Workspace agents", + description: + "Show running agents under each workspace in the sidebar, with their live status", + keywords: [ + "experimental", + "agents", + "agent", + "running", + "inline", + "sidebar", + "workspace", + "workspaces", + "status", + "toggle", + "switch", + ], + }, { id: SETTING_ITEM_ID.INTEGRATIONS_LINEAR, section: "integrations", diff --git a/apps/desktop/src/renderer/stores/workspace-agents-row.ts b/apps/desktop/src/renderer/stores/workspace-agents-row.ts new file mode 100644 index 00000000000..c0da1267ff1 --- /dev/null +++ b/apps/desktop/src/renderer/stores/workspace-agents-row.ts @@ -0,0 +1,40 @@ +import { create } from "zustand"; +import { devtools, persist } from "zustand/middleware"; + +/** + * EXPERIMENT: show running agents inline under each workspace in the sidebar. + * + * Off by default. Single source of truth for the experiment — read it + * everywhere via {@link useWorkspaceAgentsRowEnabled}. + * + * To conclude the experiment, pick an outcome and remove the other side: + * 1. This store + `useWorkspaceAgentsRowEnabled`. + * 2. The toggle UI in `ExperimentalSettings` and its `settings-search` entry + * (`EXPERIMENTAL_WORKSPACE_AGENTS`). + * 3. The flag branch in `DashboardSidebarWorkspaceDetails` (the `agents` + * section) and the `enabled` it threads into + * `useDashboardSidebarWorkspaceRunningAgents`. + */ +interface WorkspaceAgentsRowState { + // When true, running agents render inline under each workspace item. + enabled: boolean; + setEnabled: (enabled: boolean) => void; +} + +export const useWorkspaceAgentsRowStore = create()( + devtools( + persist( + (set) => ({ + enabled: false, + setEnabled: (enabled) => set({ enabled }), + }), + { name: "workspace-agents-row" }, + ), + { name: "WorkspaceAgentsRowStore" }, + ), +); + +/** Single read path for the workspace-agents-row experiment flag. */ +export function useWorkspaceAgentsRowEnabled(): boolean { + return useWorkspaceAgentsRowStore((state) => state.enabled); +}