From 360c496b9b56f6b7ced8657e59cc5188d531c9b5 Mon Sep 17 00:00:00 2001
From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Date: Fri, 7 Aug 2026 14:36:22 -0700
Subject: [PATCH] fix(agent-map): draw every visible orchestration edge, and
let the filter hide them (#13087)
The agent map dropped real parent -> child dispatch edges and mislabeled the
ones it kept.
- Remove the `child.y <= parent.y` gate on agent lineage. Both endpoints are
drawn nodes, so the relationship is real whatever the layout ranked them;
the gate silently hid edges that packing pressure placed side by side.
`lineagePath` is now direction-aware so an upward edge does not exit the
wrong side and draw back through both nodes.
- Fix the relation attribute. `parent.card.parentPaneKey ? 'subagent' : ...`
asked whether the PARENT has a parent and said nothing about the child, so
a 3-deep chain rendered solid as if it were an in-process subagent. Every
map node is a top-level pane agent -- build-dashboard-snapshot folds
subagent rows into the parent card's roster and never makes them cards --
so every card-to-card edge is an orchestration edge. Dead CSS removed.
- Add a "Orchestration links" toggle under Filter > Map content, default on,
hiding both same-worktree and cross-worktree edges while keeping the nodes.
Lineage still comes from the existing active-or-recently-settled dispatch
context; making it historical would be a main-process change and is out of
scope here by design.
Extractions are max-lines pressure, not drive-by refactors: the toolbar hit
404/400 and AgentMapCanvas 403/400, and the repo forbids new max-lines
suppressions.
---
.../AgentDashboardMapView.tsx | 4 +
.../AgentDashboardToolbar.tsx | 54 ++++++------
.../dashboard-popout/AgentMap.test.tsx | 83 ++++++++++++++++---
.../components/dashboard-popout/AgentMap.tsx | 4 +
.../dashboard-popout/AgentMapCanvas.tsx | 31 ++-----
.../AgentMapContentFilterItems.tsx | 59 +++++++++++++
.../AgentMapProjectLabel.test.tsx | 1 +
.../dashboard-popout/AgentMapScene.tsx | 56 +++++++------
.../AgentMapWorktreeRingNode.tsx | 28 +++++--
.../dashboard-popout/FilterOptionCount.tsx | 4 +
.../dashboard-popout/agent-map-canvas-zoom.ts | 25 ++++++
.../dashboard-popout/agent-map-layout.ts | 8 ++
.../components/dashboard-popout/agent-map.css | 4 -
src/renderer/src/i18n/locales/en.json | 1 +
14 files changed, 257 insertions(+), 105 deletions(-)
create mode 100644 src/renderer/src/components/dashboard-popout/AgentMapContentFilterItems.tsx
create mode 100644 src/renderer/src/components/dashboard-popout/FilterOptionCount.tsx
create mode 100644 src/renderer/src/components/dashboard-popout/agent-map-canvas-zoom.ts
diff --git a/src/renderer/src/components/dashboard-popout/AgentDashboardMapView.tsx b/src/renderer/src/components/dashboard-popout/AgentDashboardMapView.tsx
index 4010b5a0c30..5445a40ee47 100644
--- a/src/renderer/src/components/dashboard-popout/AgentDashboardMapView.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentDashboardMapView.tsx
@@ -62,6 +62,7 @@ export function AgentDashboardMapView({
}: AgentDashboardMapViewProps): React.JSX.Element {
const { agentStates, toggleAgentState, resetAgentStates } = useAgentMapStateFilter()
const [showAgentlessWorkspaces, setShowAgentlessWorkspaces] = useState(false)
+ const [showOrchestrationLinks, setShowOrchestrationLinks] = useState(true)
const agentlessWorkspaces = useMemo(
() =>
selectAgentlessMapWorkspaces({
@@ -94,6 +95,8 @@ export function AgentDashboardMapView({
showAgentlessWorkspaces={showAgentlessWorkspaces}
agentlessWorkspaceCount={agentlessWorkspaces.length}
onShowAgentlessWorkspacesChange={setShowAgentlessWorkspaces}
+ showOrchestrationLinks={showOrchestrationLinks}
+ onShowOrchestrationLinksChange={setShowOrchestrationLinks}
searchInputRef={searchInputRef}
/>
@@ -107,6 +110,7 @@ export function AgentDashboardMapView({
compact={dialogCard !== null}
selectedPaneKey={dialogCard?.paneKey}
enabledStates={agentStates}
+ showOrchestrationLinks={showOrchestrationLinks}
launchableAgentsByWorktreeId={snapshot.launchableAgentsByWorktreeId}
workspaceContextMenusEnabled={workspaceContextMenusEnabled}
onWorkspaceContextMenuOpenChange={onWorkspaceContextMenuOpenChange}
diff --git a/src/renderer/src/components/dashboard-popout/AgentDashboardToolbar.tsx b/src/renderer/src/components/dashboard-popout/AgentDashboardToolbar.tsx
index ab4e1beed6c..53fa18a7398 100644
--- a/src/renderer/src/components/dashboard-popout/AgentDashboardToolbar.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentDashboardToolbar.tsx
@@ -27,6 +27,8 @@ import {
} from './agent-board-filtering'
import { countAgentMapCards, type AgentMapState } from './agent-map-filter'
import { AgentDashboardFilterChips } from './AgentDashboardFilterChips'
+import { AgentMapContentFilterItems } from './AgentMapContentFilterItems'
+import { FilterOptionCount } from './FilterOptionCount'
import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo'
type FilterOption = { id: string; label: string; count: number; color?: string }
@@ -46,6 +48,9 @@ type AgentDashboardToolbarProps = {
showAgentlessWorkspaces?: boolean
agentlessWorkspaceCount?: number
onShowAgentlessWorkspacesChange?: (show: boolean) => void
+ /** Map-only: the dashed parent→child dispatch edges. */
+ showOrchestrationLinks?: boolean
+ onShowOrchestrationLinksChange?: (show: boolean) => void
searchInputRef: React.RefObject
}
@@ -157,10 +162,6 @@ function reviewStateLabel(state: DashboardReviewFilter): string {
}
}
-function OptionCount({ count }: { count: number }): React.JSX.Element {
- return {count}
-}
-
export function AgentDashboardToolbar({
cards,
filterOptions,
@@ -175,6 +176,8 @@ export function AgentDashboardToolbar({
showAgentlessWorkspaces,
agentlessWorkspaceCount = 0,
onShowAgentlessWorkspacesChange,
+ showOrchestrationLinks,
+ onShowOrchestrationLinksChange,
searchInputRef
}: AgentDashboardToolbarProps): React.JSX.Element {
const isMac = navigator.userAgent.includes('Mac')
@@ -190,7 +193,9 @@ export function AgentDashboardToolbar({
const activeCount =
activeDashboardFilterCount(filters) +
mutedStateCount +
- (showAgentlessWorkspaces === true ? 1 : 0)
+ (showAgentlessWorkspaces === true ? 1 : 0) +
+ // Links show by default, so only hiding them is a deviation worth badging.
+ (showOrchestrationLinks === false ? 1 : 0)
const toggleProject = (id: string): void =>
onFiltersChange({ ...filters, projects: toggleDashboardFilter(filters.projects, id) })
const toggleStatus = (id: string): void =>
@@ -207,6 +212,7 @@ export function AgentDashboardToolbar({
onFiltersChange({ projects: [], workspaceStatuses: [], reviewStates: [] })
onAgentStatesReset?.()
onShowAgentlessWorkspacesChange?.(false)
+ onShowOrchestrationLinksChange?.(true)
}
const reviewLabel = (id: DashboardReviewFilter): string =>
translate('dashboardPopout.filters.reviewChip', 'Review: {{state}}', {
@@ -274,26 +280,16 @@ export function AgentDashboardToolbar({
- {showAgentlessWorkspaces !== undefined && onShowAgentlessWorkspacesChange ? (
- <>
-
- {translate('dashboardPopout.map.filters.workspaceVisibility', 'Map content')}
-
- onShowAgentlessWorkspacesChange(checked === true)}
- onSelect={(event) => event.preventDefault()}
- >
-
- {translate(
- 'dashboardPopout.map.filters.agentlessWorkspaces',
- 'Workspaces without agents'
- )}
-
-
-
-
- >
+ {showAgentlessWorkspaces !== undefined &&
+ onShowAgentlessWorkspacesChange &&
+ onShowOrchestrationLinksChange ? (
+
) : null}
{agentStates && agentStateCounts ? (
<>
@@ -309,7 +305,7 @@ export function AgentDashboardToolbar({
>
{agentStateLabel(state)}
-
+
))}
@@ -326,7 +322,7 @@ export function AgentDashboardToolbar({
onSelect={(event) => event.preventDefault()}
>
{option.label}
-
+
))}
@@ -348,7 +344,7 @@ export function AgentDashboardToolbar({
>
{option.label}
-
+
)
})}
@@ -364,7 +360,7 @@ export function AgentDashboardToolbar({
onSelect={(event) => event.preventDefault()}
>
{reviewStateLabel(option)}
-
+
))}
diff --git a/src/renderer/src/components/dashboard-popout/AgentMap.test.tsx b/src/renderer/src/components/dashboard-popout/AgentMap.test.tsx
index 483e9d1e9ed..60f587bc907 100644
--- a/src/renderer/src/components/dashboard-popout/AgentMap.test.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentMap.test.tsx
@@ -49,6 +49,7 @@ function renderMap(
compact = false,
workspaceContextMenusEnabled = false,
enabledStates,
+ showOrchestrationLinks,
launchableAgentsByWorktreeId,
onSpawnAgent,
onSleepWorkspace
@@ -58,6 +59,7 @@ function renderMap(
compact?: boolean
workspaceContextMenusEnabled?: boolean
enabledStates?: ReadonlySet
+ showOrchestrationLinks?: boolean
launchableAgentsByWorktreeId?: Record
onSpawnAgent?: (args: DashboardSpawnAgentArgs) => void
onSleepWorkspace?: (args: DashboardSleepWorkspaceArgs) => void
@@ -72,6 +74,7 @@ function renderMap(
compact={compact}
workspaceContextMenusEnabled={workspaceContextMenusEnabled}
enabledStates={enabledStates}
+ showOrchestrationLinks={showOrchestrationLinks}
launchableAgentsByWorktreeId={launchableAgentsByWorktreeId}
onSpawnAgent={onSpawnAgent}
onSleepWorkspace={onSleepWorkspace}
@@ -350,18 +353,19 @@ describe('AgentMap', () => {
})
const { container } = renderMap([parent, child, nested, orphan])
const workerLink = container.querySelector('[data-child-pane-key="child"]')
- const subagentLink = container.querySelector('[data-child-pane-key="nested"]')
+ const nestedLink = container.querySelector('[data-child-pane-key="nested"]')
expect(screen.getByRole('button', { name: /Coordinator/ })).toBeInTheDocument()
expect(screen.getByRole('button', { name: /^Worker/ })).toBeInTheDocument()
expect(container.querySelectorAll('[data-agent-map-lineage-link]')).toHaveLength(2)
expect(workerLink).toHaveClass('agent-map-lineage-link')
- expect(workerLink).not.toHaveClass('is-subagent')
expect(workerLink).toHaveAttribute('data-agent-map-lineage-relation', 'orchestration')
expect(workerLink).toHaveAttribute('data-parent-pane-key', 'parent')
- expect(subagentLink).toHaveClass('agent-map-lineage-link', 'is-subagent')
- expect(subagentLink).toHaveAttribute('data-agent-map-lineage-relation', 'subagent')
- expect(subagentLink).toHaveAttribute('data-parent-pane-key', 'child')
+ // Why orchestration, not subagent: `nested` is a card, and in-process subagents
+ // never become cards — so a grandchild dispatch is still an orchestration edge.
+ expect(nestedLink).toHaveClass('agent-map-lineage-link')
+ expect(nestedLink).toHaveAttribute('data-agent-map-lineage-relation', 'orchestration')
+ expect(nestedLink).toHaveAttribute('data-parent-pane-key', 'child')
})
it('connects spawned workers across worktree rings', () => {
@@ -387,13 +391,12 @@ describe('AgentMap', () => {
})
const { container } = renderMap([parent, child, nested])
const workerLink = container.querySelector('[data-child-pane-key="child"]')
- const subagentLink = container.querySelector('[data-child-pane-key="nested"]')
+ const nestedLink = container.querySelector('[data-child-pane-key="nested"]')
expect(workerLink).toHaveClass('agent-map-lineage-link', 'is-cross-worktree')
- expect(workerLink).not.toHaveClass('is-subagent')
expect(workerLink).toHaveAttribute('data-agent-map-lineage-relation', 'orchestration')
- expect(subagentLink).toHaveClass('agent-map-lineage-link', 'is-cross-worktree', 'is-subagent')
- expect(subagentLink).toHaveAttribute('data-agent-map-lineage-relation', 'subagent')
+ expect(nestedLink).toHaveClass('agent-map-lineage-link', 'is-cross-worktree')
+ expect(nestedLink).toHaveAttribute('data-agent-map-lineage-relation', 'orchestration')
})
it('keeps lineage styling to one lightweight path per relationship at fleet scale', () => {
@@ -409,13 +412,71 @@ describe('AgentMap', () => {
expect(container.querySelectorAll('[data-agent-map-lineage-link]')).toHaveLength(239)
expect(
container.querySelectorAll('[data-agent-map-lineage-relation="orchestration"]')
- ).toHaveLength(1)
+ ).toHaveLength(239)
expect(container.querySelectorAll('[data-agent-map-lineage-relation="subagent"]')).toHaveLength(
- 238
+ 0
)
expect(container.querySelectorAll('filter, animate, animateTransform')).toHaveLength(0)
})
+ it('hides orchestration links when the filter turns them off, keeping the agents', () => {
+ const sameWorktree = [
+ card({ paneKey: 'parent', conversationName: 'Coordinator' }),
+ card({ paneKey: 'child', parentPaneKey: 'parent', conversationName: 'Worker' })
+ ]
+ const crossWorktree = [
+ card({ paneKey: 'far-parent', worktreeId: 'wt-a', worktreeName: 'A' }),
+ card({
+ paneKey: 'far-child',
+ worktreeId: 'wt-b',
+ worktreeName: 'B',
+ parentPaneKey: 'far-parent'
+ })
+ ]
+ const cards = [...sameWorktree, ...crossWorktree]
+
+ const shown = renderMap(cards)
+ expect(shown.container.querySelectorAll('[data-agent-map-lineage-link]')).toHaveLength(2)
+ cleanup()
+
+ const hidden = renderMap(cards, { showOrchestrationLinks: false })
+ // Both same-worktree and cross-worktree edges go; the nodes themselves stay.
+ expect(hidden.container.querySelectorAll('[data-agent-map-lineage-link]')).toHaveLength(0)
+ expect(screen.getByRole('button', { name: /Coordinator/ })).toBeInTheDocument()
+ expect(screen.getByRole('button', { name: /^Worker/ })).toBeInTheDocument()
+ })
+
+ it('draws no lineage edge for an agent that lists itself as its own parent', () => {
+ const { container } = renderMap([
+ card({ paneKey: 'self', parentPaneKey: 'self', conversationName: 'Self parent' }),
+ card({ paneKey: 'other', conversationName: 'Unrelated' })
+ ])
+
+ expect(container.querySelectorAll('[data-agent-map-lineage-link]')).toHaveLength(0)
+ expect(screen.getByRole('button', { name: /Self parent/ })).toBeInTheDocument()
+ })
+
+ it('connects lineage between visible nodes even when the child is not ranked below its parent', () => {
+ // A 2-cycle cannot be ranked consistently, so the bounded layout (>256 agents)
+ // must place one of its edges pointing upward. Both nodes are drawn, so both
+ // edges must be too — the old y-ordering gate silently dropped the upward one.
+ const cards = [
+ card({ paneKey: 'cycle-a', parentPaneKey: 'cycle-b', conversationName: 'Cycle A' }),
+ card({ paneKey: 'cycle-b', parentPaneKey: 'cycle-a', conversationName: 'Cycle B' }),
+ ...Array.from({ length: 255 }, (_, index) =>
+ card({
+ paneKey: `filler-${index}`,
+ parentPaneKey: index === 0 ? undefined : `filler-${index - 1}`,
+ conversationName: `Filler ${index}`
+ })
+ )
+ ]
+ const { container } = renderMap(cards, { selectedPaneKey: 'cycle-a' })
+
+ expect(container.querySelector('[data-child-pane-key="cycle-a"]')).not.toBeNull()
+ expect(container.querySelector('[data-child-pane-key="cycle-b"]')).not.toBeNull()
+ })
+
it('connects visible child worktrees beneath their parent ring', () => {
const { container } = renderMap([
card({ paneKey: 'parent', worktreeId: 'parent-worktree', worktreeName: 'Parent' }),
diff --git a/src/renderer/src/components/dashboard-popout/AgentMap.tsx b/src/renderer/src/components/dashboard-popout/AgentMap.tsx
index 4e9c3c413ca..6bce95eaeac 100644
--- a/src/renderer/src/components/dashboard-popout/AgentMap.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentMap.tsx
@@ -31,6 +31,8 @@ type AgentMapProps = {
/** Owned by the board so the shared toolbar filter can drive it. Defaults to
* every state, i.e. the map shows whatever it is handed. */
enabledStates?: ReadonlySet
+ /** Owned by the board's filter menu. Defaults to shown. */
+ showOrchestrationLinks?: boolean
launchableAgentsByWorktreeId?: Record
workspaceContextMenusEnabled?: boolean
onWorkspaceContextMenuOpenChange?: (open: boolean) => void
@@ -72,6 +74,7 @@ export function AgentMap({
compact = false,
selectedPaneKey = null,
enabledStates = ALL_AGENT_STATES,
+ showOrchestrationLinks = true,
launchableAgentsByWorktreeId,
workspaceContextMenusEnabled = false,
onWorkspaceContextMenuOpenChange,
@@ -169,6 +172,7 @@ export function AgentMap({
repoIconsByRepoId={repoIconsByRepoId}
selectedPaneKey={selectedPaneKey}
allowAggregation
+ showOrchestrationLinks={showOrchestrationLinks}
launchableAgentsByWorktreeId={launchableAgentsByWorktreeId}
workspaceContextMenusEnabled={workspaceContextMenusEnabled}
onWorkspaceContextMenuOpenChange={onWorkspaceContextMenuOpenChange}
diff --git a/src/renderer/src/components/dashboard-popout/AgentMapCanvas.tsx b/src/renderer/src/components/dashboard-popout/AgentMapCanvas.tsx
index 33cd767cc6a..06e52c84453 100644
--- a/src/renderer/src/components/dashboard-popout/AgentMapCanvas.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentMapCanvas.tsx
@@ -16,13 +16,9 @@ import type {
} from '../../../../shared/dashboard-snapshot'
import type { RepoIcon } from '../../../../shared/repo-icon'
import type { TuiAgent } from '../../../../shared/types'
-import {
- AGENT_MAP_AGENT_RADIUS,
- type AgentMapAgentNode,
- type AgentMapProjectRing,
- type AgentMapLayout
-} from './agent-map-layout'
+import type { AgentMapAgentNode, AgentMapProjectRing, AgentMapLayout } from './agent-map-layout'
import { AgentMapScene } from './AgentMapScene'
+import { agentFocusZoom, clamp, MAX_ZOOM, MIN_ZOOM } from './agent-map-canvas-zoom'
import { AgentMapViewportControls } from './AgentMapViewportControls'
import {
agentMapAgents,
@@ -35,10 +31,7 @@ import { useAgentMapCanvasSize } from './useAgentMapCanvasSize'
import { useAgentMapSelectedFocus } from './useAgentMapSelectedFocus'
import { useAgentMapViewportTransition } from './useAgentMapViewportTransition'
-const MIN_ZOOM = 0.7
-const MAX_ZOOM = 24
const AGENT_FOCUS_DURATION_MS = 240
-const AGENT_FOCUS_RADIUS_PX = 24
type Point = { x: number; y: number }
@@ -52,6 +45,7 @@ type AgentMapCanvasProps = {
repoIconsByRepoId?: Record
selectedPaneKey: string | null
allowAggregation: boolean
+ showOrchestrationLinks: boolean
launchableAgentsByWorktreeId?: Record
workspaceContextMenusEnabled?: boolean
onWorkspaceContextMenuOpenChange?: (open: boolean) => void
@@ -60,23 +54,6 @@ type AgentMapCanvasProps = {
onSleepWorkspace?: (args: DashboardSleepWorkspaceArgs) => void
}
-function clamp(value: number, minimum: number, maximum: number): number {
- return Math.max(minimum, Math.min(maximum, value))
-}
-
-function agentFocusZoom(layout: AgentMapLayout, width: number, height: number): number {
- const aspect = width / Math.max(1, height)
- const baseWidth = Math.max(layout.width, layout.height * aspect)
- return clamp(
- Math.max(
- 2,
- (baseWidth * AGENT_FOCUS_RADIUS_PX) / (Math.max(1, width) * AGENT_MAP_AGENT_RADIUS)
- ),
- MIN_ZOOM,
- MAX_ZOOM
- )
-}
-
export const AgentMapCanvas = forwardRef(
function AgentMapCanvas(
{
@@ -84,6 +61,7 @@ export const AgentMapCanvas = forwardRef void
+ showOrchestrationLinks: boolean
+ onShowOrchestrationLinksChange: (show: boolean) => void
+}
+
+/**
+ * The map-only "Map content" rows of the shared dashboard filter menu. These
+ * govern what the map draws rather than which cards survive filtering, so they
+ * sit apart from the project/status/review sections.
+ */
+export function AgentMapContentFilterItems({
+ showAgentlessWorkspaces,
+ agentlessWorkspaceCount,
+ onShowAgentlessWorkspacesChange,
+ showOrchestrationLinks,
+ onShowOrchestrationLinksChange
+}: AgentMapContentFilterItemsProps): React.JSX.Element {
+ return (
+ <>
+
+ {translate('dashboardPopout.map.filters.workspaceVisibility', 'Map content')}
+
+ onShowAgentlessWorkspacesChange(checked === true)}
+ onSelect={(event) => event.preventDefault()}
+ >
+
+ {translate(
+ 'dashboardPopout.map.filters.agentlessWorkspaces',
+ 'Workspaces without agents'
+ )}
+
+
+
+ onShowOrchestrationLinksChange(checked === true)}
+ onSelect={(event) => event.preventDefault()}
+ >
+
+ {translate('dashboardPopout.map.filters.orchestrationLinks', 'Orchestration links')}
+
+
+
+ >
+ )
+}
diff --git a/src/renderer/src/components/dashboard-popout/AgentMapProjectLabel.test.tsx b/src/renderer/src/components/dashboard-popout/AgentMapProjectLabel.test.tsx
index 5cbf41b7abb..7846e010c78 100644
--- a/src/renderer/src/components/dashboard-popout/AgentMapProjectLabel.test.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentMapProjectLabel.test.tsx
@@ -41,6 +41,7 @@ describe('AgentMapScene project labels', () => {
mapScale={0.5}
selectedPaneKey={null}
allowAggregation
+ showOrchestrationLinks
nodeRefs={{ current: new Map() }}
onSelectAgent={vi.fn()}
onAgentKeyDown={vi.fn()}
diff --git a/src/renderer/src/components/dashboard-popout/AgentMapScene.tsx b/src/renderer/src/components/dashboard-popout/AgentMapScene.tsx
index 2719f4f7921..0dc09b6f0dd 100644
--- a/src/renderer/src/components/dashboard-popout/AgentMapScene.tsx
+++ b/src/renderer/src/components/dashboard-popout/AgentMapScene.tsx
@@ -10,7 +10,7 @@ import type {
AgentMapProjectRing,
AgentMapWorktreeRing
} from './agent-map-layout'
-import { shouldAggregateAgentMapWorktree } from './agent-map-layout'
+import { AGENT_MAP_LINEAGE_RELATION, shouldAggregateAgentMapWorktree } from './agent-map-layout'
import { selectVisibleAgentMapLabels } from './agent-map-label-declutter'
import { AgentMapWorktreeLabel } from './AgentMapWorktreeLabel'
import { AgentMapWorktreeRingNode } from './AgentMapWorktreeRingNode'
@@ -23,6 +23,7 @@ type AgentMapSceneProps = {
mapScale: number
selectedPaneKey: string | null
allowAggregation: boolean
+ showOrchestrationLinks: boolean
launchableAgentsByWorktreeId?: Record
nodeRefs: MutableRefObject