{"version":3,"file":"fleet.d.ts","sourceRoot":"","sources":["../../../src/tui/fleet.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { AgentToolResult } from \"@lpb-work/pi-agent-core\";\nimport { type ExtensionContext, getMarkdownTheme } from \"@lpb-work/pi-coding-agent\";\nimport {\n\ttype Component,\n\tKey,\n\ttype MarkdownTheme,\n\tmatchesKey,\n\ttruncateToWidth,\n\tvisibleWidth,\n\twrapTextWithAnsi,\n} from \"@lpb-work/pi-tui\";\nimport { handleHerdrInspectorAction } from \"../inspectors/herdr/actions.ts\";\nimport { type AsyncRunSummary, listAsyncRuns } from \"../runs/background/async-status.ts\";\nimport type { SteerDeliveryMode } from \"../runs/background/control-channel.ts\";\nimport { formatAsyncRunTranscript } from \"../runs/background/fleet-view.ts\";\nimport { steerAsyncRun } from \"../runs/foreground/async-steering-action.ts\";\nimport { stopAsyncRun } from \"../runs/foreground/async-stop-action.ts\";\nimport { contextModeBadge, contextModeLabel } from \"../runs/shared/context-mode.ts\";\nimport { getArtifactPaths, getArtifactsDir } from \"../shared/artifacts.ts\";\nimport { formatDuration, formatModelThinking, formatTokens, shortenPath } from \"../shared/formatters.ts\";\nimport {\n\ttype AsyncJobState,\n\ttype Details,\n\tDIRS,\n\ttype ForegroundChildControl,\n\ttype ForegroundResumeChild,\n\ttype ForegroundResumeRun,\n\ttype ForegroundRunControl,\n\ttype SubagentState,\n} from \"../shared/types.ts\";\nimport { readStatus } from \"../shared/utils.ts\";\nimport { FLEET_STATUS_WIDGET_KEY } from \"./fleet-status.ts\";\nimport { type FleetTranscript, readFleetTranscript, renderFleetTranscript } from \"./fleet-transcript.ts\";\n\nconst REFRESH_MS = 750;\nconst MAX_RECENT_ASYNC_RUNS = 20;\nconst MAX_FLEET_HISTORY_CANDIDATES = 100;\nconst TRANSCRIPT_LINES = 200;\n\ntype Theme = ExtensionContext[\"ui\"][\"theme\"];\ntype FleetTui = {\n\tterminal?: { rows: number };\n\trequestRender(): void;\n};\ntype AsyncStep = AsyncRunSummary[\"steps\"][number];\n\nexport type FleetItem = (\n\t| {\n\t\t\tkey: string;\n\t\t\tkind: \"foreground-active\";\n\t\t\trunId: string;\n\t\t\tindex?: number;\n\t\t\tagent: string;\n\t\t\tstate: \"running\";\n\t\t\tupdatedAt: number;\n\t\t\tcontrol: ForegroundRunControl;\n\t\t\tactiveChild?: ForegroundChildControl;\n\t  }\n\t| {\n\t\t\tkey: string;\n\t\t\tkind: \"foreground-recent\";\n\t\t\trunId: string;\n\t\t\tindex: number;\n\t\t\tagent: string;\n\t\t\tstate: ForegroundResumeChild[\"status\"];\n\t\t\tupdatedAt: number;\n\t\t\trun: ForegroundResumeRun;\n\t\t\tchild: ForegroundResumeChild;\n\t  }\n\t| {\n\t\t\tkey: string;\n\t\t\tkind: \"async\";\n\t\t\trunId: string;\n\t\t\tindex?: number;\n\t\t\tagent: string;\n\t\t\tstate: string;\n\t\t\tupdatedAt: number;\n\t\t\trun: AsyncRunSummary;\n\t\t\tstep?: AsyncStep;\n\t  }\n) & { description?: string };\n\nexport interface FleetSnapshot {\n\titems: FleetItem[];\n\terror?: string;\n}\n\nexport interface FleetActionResult {\n\ttext: string;\n\tisError?: boolean;\n}\n\nexport interface FleetActionHandlers {\n\tsteer(input: {\n\t\trunId: string;\n\t\tasyncDir: string;\n\t\tindex?: number;\n\t\tmessage: string;\n\t\tmode: SteerDeliveryMode;\n\t}): Promise<FleetActionResult>;\n\tstop(input: { runId: string; asyncDir: string; index?: number }): Promise<FleetActionResult> | FleetActionResult;\n\tinspect?(input: { runId: string; asyncDir: string; index?: number }): Promise<FleetActionResult>;\n}\n\nexport interface FleetViewOptions {\n\tasyncDirRoot?: string;\n\tresultsDir?: string;\n\trefreshMs?: number;\n\tinitialKey?: string;\n\tmarkdownTheme?: MarkdownTheme;\n\tactions?: FleetActionHandlers;\n}\n\nfunction belongsToCurrentSession(sessionId: string | undefined, currentSessionId: string | null): boolean {\n\treturn !currentSessionId || sessionId === currentSessionId;\n}\n\nfunction trackedJobSummary(job: AsyncJobState): AsyncRunSummary {\n\tconst startedAt = job.startedAt ?? job.updatedAt ?? Date.now();\n\treturn {\n\t\tid: job.asyncId,\n\t\tasyncDir: job.asyncDir,\n\t\t...(job.sessionId ? { sessionId: job.sessionId } : {}),\n\t\tstate: job.status,\n\t\tactivityState: job.activityState,\n\t\tlastActivityAt: job.lastActivityAt,\n\t\tcurrentTool: job.currentTool,\n\t\tcurrentToolStartedAt: job.currentToolStartedAt,\n\t\tcurrentPath: job.currentPath,\n\t\tturnCount: job.turnCount,\n\t\ttoolCount: job.toolCount,\n\t\tsteering: job.steering,\n\t\tmode: job.mode ?? \"single\",\n\t\t...(job.context ? { context: job.context } : {}),\n\t\t...(job.cwd ? { cwd: job.cwd } : {}),\n\t\tstartedAt,\n\t\t...(job.updatedAt !== undefined ? { lastUpdate: job.updatedAt } : {}),\n\t\t...(job.timeoutMs !== undefined ? { timeoutMs: job.timeoutMs } : {}),\n\t\t...(job.deadlineAt !== undefined ? { deadlineAt: job.deadlineAt } : {}),\n\t\t...(job.timedOut !== undefined ? { timedOut: job.timedOut } : {}),\n\t\t...(job.stopped !== undefined ? { stopped: job.stopped } : {}),\n\t\t...(job.turnBudget ? { turnBudget: job.turnBudget } : {}),\n\t\t...(job.turnBudgetExceeded !== undefined ? { turnBudgetExceeded: job.turnBudgetExceeded } : {}),\n\t\t...(job.wrapUpRequested !== undefined ? { wrapUpRequested: job.wrapUpRequested } : {}),\n\t\t...(job.currentStep !== undefined ? { currentStep: job.currentStep } : {}),\n\t\t...(job.chainStepCount !== undefined ? { chainStepCount: job.chainStepCount } : {}),\n\t\t...(job.parallelGroups?.length ? { parallelGroups: job.parallelGroups } : {}),\n\t\tsteps: (\n\t\t\tjob.steps ??\n\t\t\tjob.agents?.map((agent, index) => ({\n\t\t\t\tagent,\n\t\t\t\tindex,\n\t\t\t\tstatus: job.status === \"queued\" ? (\"pending\" as const) : job.status,\n\t\t\t})) ??\n\t\t\t[]\n\t\t).map((step, index) => ({\n\t\t\t...step,\n\t\t\tindex: step.index ?? index,\n\t\t})),\n\t\t...(job.sessionDir ? { sessionDir: job.sessionDir } : {}),\n\t\t...(job.outputFile ? { outputFile: job.outputFile } : {}),\n\t\t...(job.totalTokens ? { totalTokens: job.totalTokens } : {}),\n\t\t...(job.sessionFile ? { sessionFile: job.sessionFile } : {}),\n\t\t...(job.nestedChildren?.length ? { nestedChildren: job.nestedChildren } : {}),\n\t};\n}\n\nfunction asyncItems(run: AsyncRunSummary, description?: string): FleetItem[] {\n\tconst updatedAt = run.lastUpdate ?? run.endedAt ?? run.startedAt;\n\tif (run.steps.length === 0 || run.mode === \"workflow\") {\n\t\treturn [\n\t\t\t{\n\t\t\t\tkey: `async:${run.id}`,\n\t\t\t\tkind: \"async\",\n\t\t\t\trunId: run.id,\n\t\t\t\tagent: run.mode,\n\t\t\t\tstate: run.state,\n\t\t\t\tupdatedAt,\n\t\t\t\trun,\n\t\t\t\t...(description ? { description } : {}),\n\t\t\t},\n\t\t];\n\t}\n\treturn run.steps.map((step) => ({\n\t\tkey: `async:${run.id}:${step.index}`,\n\t\tkind: \"async\" as const,\n\t\trunId: run.id,\n\t\tindex: step.index,\n\t\tagent: step.label ? `${step.label} (${step.agent})` : step.agent,\n\t\tstate: step.status,\n\t\tupdatedAt: step.lastActivityAt ?? updatedAt,\n\t\trun,\n\t\tstep,\n\t\t...(description ? { description } : {}),\n\t}));\n}\n\nfunction orderFleetAsyncRuns(runs: AsyncRunSummary[], terminalLimit: number): AsyncRunSummary[] {\n\tconst updatedAt = (run: AsyncRunSummary) => run.lastUpdate ?? run.endedAt ?? run.startedAt;\n\tconst byNewest = (left: AsyncRunSummary, right: AsyncRunSummary) => updatedAt(right) - updatedAt(left);\n\tconst active = runs.filter((run) => run.state === \"queued\" || run.state === \"running\").sort(byNewest);\n\tconst terminal = runs.filter((run) => run.state !== \"queued\" && run.state !== \"running\").sort(byNewest);\n\treturn [...active, ...terminal.slice(0, terminalLimit)];\n}\n\nexport function collectFleetSnapshot(\n\tstate: SubagentState,\n\toptions: { asyncDirRoot?: string; resultsDir?: string; limit?: number } = {},\n): FleetSnapshot {\n\tconst items: FleetItem[] = [];\n\tconst activeForegroundIds = new Set<string>();\n\tfor (const control of [...state.foregroundControls.values()].sort(\n\t\t(left, right) => right.updatedAt - left.updatedAt,\n\t)) {\n\t\tactiveForegroundIds.add(control.runId);\n\t\tif (control.activeChildren) {\n\t\t\tfor (const child of [...control.activeChildren.values()].sort((left, right) => left.index - right.index)) {\n\t\t\t\titems.push({\n\t\t\t\t\tkey: `foreground-active:${control.runId}:${child.index}`,\n\t\t\t\t\tkind: \"foreground-active\",\n\t\t\t\t\trunId: control.runId,\n\t\t\t\t\tindex: child.index,\n\t\t\t\t\tagent: child.agent,\n\t\t\t\t\tstate: \"running\",\n\t\t\t\t\tupdatedAt: child.updatedAt,\n\t\t\t\t\tcontrol,\n\t\t\t\t\tactiveChild: child,\n\t\t\t\t\t...(child.description ? { description: child.description } : {}),\n\t\t\t\t});\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\titems.push({\n\t\t\tkey: `foreground-active:${control.runId}:${control.currentIndex ?? 0}`,\n\t\t\tkind: \"foreground-active\",\n\t\t\trunId: control.runId,\n\t\t\t...(control.currentIndex !== undefined ? { index: control.currentIndex } : {}),\n\t\t\tagent: control.currentAgent ?? control.mode,\n\t\t\tstate: \"running\",\n\t\t\tupdatedAt: control.updatedAt,\n\t\t\tcontrol,\n\t\t\t...(control.description ? { description: control.description } : {}),\n\t\t});\n\t}\n\n\tlet error: string | undefined;\n\ttry {\n\t\tlet runs: AsyncRunSummary[];\n\t\tconst descriptions = new Map<string, string>();\n\t\tconst tracked = [...(state.fleetJobs ?? state.asyncJobs).values()].filter((job) =>\n\t\t\tbelongsToCurrentSession(job.sessionId, state.currentSessionId),\n\t\t);\n\t\tconst byUpdate = (left: AsyncJobState, right: AsyncJobState) =>\n\t\t\t(right.updatedAt ?? right.startedAt ?? 0) - (left.updatedAt ?? left.startedAt ?? 0);\n\t\tconst active = tracked.filter((job) => job.status === \"queued\" || job.status === \"running\").sort(byUpdate);\n\t\tconst recent = tracked\n\t\t\t.filter((job) => job.status !== \"queued\" && job.status !== \"running\")\n\t\t\t.sort(byUpdate)\n\t\t\t.slice(0, options.limit ?? MAX_RECENT_ASYNC_RUNS);\n\t\tconst trackedRuns: AsyncRunSummary[] = [];\n\t\tfor (const job of [...active, ...recent]) {\n\t\t\ttry {\n\t\t\t\ttrackedRuns.push(trackedJobSummary(job));\n\t\t\t\tif (job.description) descriptions.set(job.asyncId, job.description);\n\t\t\t} catch (cause) {\n\t\t\t\terror = `Failed to inspect async run '${job.asyncId}': ${cause instanceof Error ? cause.message : String(cause)}`;\n\t\t\t}\n\t\t}\n\t\tif (options.asyncDirRoot !== undefined) {\n\t\t\tconst trackedIds = new Set(trackedRuns.map((run) => run.id));\n\t\t\tconst history = listAsyncRuns(options.asyncDirRoot, {\n\t\t\t\t...(state.currentSessionId ? { sessionId: state.currentSessionId } : {}),\n\t\t\t\tentryLimit: MAX_FLEET_HISTORY_CANDIDATES,\n\t\t\t\tresultsDir: options.resultsDir ?? DIRS.results,\n\t\t\t\treconcile: false,\n\t\t\t}).filter((run) => !trackedIds.has(run.id));\n\t\t\truns = [...trackedRuns, ...history];\n\t\t} else {\n\t\t\truns = trackedRuns;\n\t\t}\n\t\tfor (const run of orderFleetAsyncRuns(runs, options.limit ?? MAX_RECENT_ASYNC_RUNS)) {\n\t\t\titems.push(...asyncItems(run, descriptions.get(run.id)));\n\t\t}\n\t} catch (cause) {\n\t\terror = cause instanceof Error ? cause.message : String(cause);\n\t}\n\n\tconst recentForeground = [...(state.foregroundRuns?.values() ?? [])]\n\t\t.filter(\n\t\t\t(run) => belongsToCurrentSession(run.sessionId, state.currentSessionId) && !activeForegroundIds.has(run.runId),\n\t\t)\n\t\t.sort((left, right) => right.updatedAt - left.updatedAt);\n\tfor (const run of recentForeground) {\n\t\tfor (const child of run.children) {\n\t\t\titems.push({\n\t\t\t\tkey: `foreground-recent:${run.runId}:${child.index}`,\n\t\t\t\tkind: \"foreground-recent\",\n\t\t\t\trunId: run.runId,\n\t\t\t\tindex: child.index,\n\t\t\t\tagent: child.agent,\n\t\t\t\tstate: child.status,\n\t\t\t\tupdatedAt: child.updatedAt ?? run.updatedAt,\n\t\t\t\trun,\n\t\t\t\tchild,\n\t\t\t});\n\t\t}\n\t}\n\treturn { items, ...(error ? { error } : {}) };\n}\n\nfunction statusGlyph(item: FleetItem, theme: Theme): string {\n\tif (item.state === \"running\") return theme.fg(\"accent\", \"●\");\n\tif (item.state === \"queued\" || item.state === \"pending\") return theme.fg(\"muted\", \"◦\");\n\tif (item.state === \"complete\" || item.state === \"completed\") return theme.fg(\"success\", \"✓\");\n\tif (item.state === \"paused\" || item.state === \"stopped\" || item.state === \"detached\")\n\t\treturn theme.fg(\"warning\", \"■\");\n\treturn theme.fg(\"error\", \"✗\");\n}\n\nfunction foregroundActiveDetail(item: Extract<FleetItem, { kind: \"foreground-active\" }>): string[] {\n\tconst { control } = item;\n\tconst live = item.activeChild ?? control;\n\tconst modelThinking = formatModelThinking(live.model, live.thinking);\n\tconst lines = [\n\t\t`Run: ${item.runId}`,\n\t\t\"Source: foreground\",\n\t\t`State: running`,\n\t\t`Mode: ${control.mode}`,\n\t\titem.index !== undefined ? `Child: ${item.index} (${item.agent})` : `Agent: ${item.agent}`,\n\t\tmodelThinking ? `Model: ${modelThinking}` : undefined,\n\t\t`Started: ${new Date(live.startedAt).toISOString()}`,\n\t\tlive.currentTool\n\t\t\t? `Current tool: ${live.currentTool}${live.currentPath ? ` · ${shortenPath(live.currentPath)}` : \"\"}`\n\t\t\t: undefined,\n\t\tlive.turnCount !== undefined ? `Turns: ${live.turnCount}` : undefined,\n\t\tlive.toolCount !== undefined ? `Tools: ${live.toolCount}` : undefined,\n\t\tlive.tokens !== undefined ? `Tokens: ${formatTokens(live.tokens)}` : undefined,\n\t\t\"\",\n\t\t\"Transcript\",\n\t\t\"Live foreground output remains in the expanded subagent tool result. Persisted output and session paths appear here after the child settles.\",\n\t];\n\treturn lines.filter((line): line is string => line !== undefined);\n}\n\nfunction foregroundRecentDetail(item: Extract<FleetItem, { kind: \"foreground-recent\" }>): string[] {\n\tconst { child, run } = item;\n\tconst outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;\n\tconst modelThinking = formatModelThinking(child.model, child.thinking);\n\tconst lines = [\n\t\t`Run: ${item.runId}`,\n\t\t\"Source: foreground\",\n\t\t`State: ${child.status}`,\n\t\t`Mode: ${run.mode}`,\n\t\t`Child: ${child.index} (${child.agent})${contextModeLabel(child.context) ? ` ${contextModeLabel(child.context)}` : \"\"}`,\n\t\tmodelThinking ? `Model: ${modelThinking}` : undefined,\n\t\t`Updated: ${new Date(child.updatedAt ?? run.updatedAt).toISOString()}`,\n\t\toutputPath ? `Output: ${outputPath}` : undefined,\n\t\tchild.sessionFile ? `Session: ${child.sessionFile}` : undefined,\n\t\tchild.transcriptPath ? `Transcript file: ${child.transcriptPath}` : undefined,\n\t\tchild.error ? `Error: ${child.error}` : undefined,\n\t\tchild.outputSaveError ? `Output warning: ${child.outputSaveError}` : undefined,\n\t\tchild.transcriptError ? `Transcript warning: ${child.transcriptError}` : undefined,\n\t\t\"\",\n\t\t\"Result transcript tail\",\n\t];\n\tconst outputLines = (child.finalOutput ?? \"\")\n\t\t.split(/\\r?\\n/)\n\t\t.filter((line) => line.trim())\n\t\t.slice(-TRANSCRIPT_LINES);\n\tlines.push(...(outputLines.length ? outputLines : [\"(no recovered output available)\"]));\n\treturn lines.filter((line): line is string => line !== undefined);\n}\n\nfunction asyncDetail(item: Extract<FleetItem, { kind: \"async\" }>): string[] {\n\tconst status = readStatus(item.run.asyncDir);\n\tif (status) {\n\t\treturn formatAsyncRunTranscript(status, item.run.asyncDir, { index: item.index, lines: TRANSCRIPT_LINES }).split(\n\t\t\t\"\\n\",\n\t\t);\n\t}\n\tconst outputPath = item.index !== undefined ? path.join(item.run.asyncDir, `output-${item.index}.log`) : undefined;\n\treturn [\n\t\t`Run: ${item.runId}`,\n\t\t\"Source: async\",\n\t\t`State: ${item.state}`,\n\t\t`Mode: ${item.run.mode}${contextModeLabel(item.run.context) ? ` ${contextModeLabel(item.run.context)}` : \"\"}`,\n\t\titem.index !== undefined\n\t\t\t? `Child: ${item.index} (${item.agent})${contextModeLabel(item.step?.context) ? ` ${contextModeLabel(item.step?.context)}` : \"\"}`\n\t\t\t: `Agent: ${item.agent}${contextModeLabel(item.run.context) ? ` ${contextModeLabel(item.run.context)}` : \"\"}`,\n\t\toutputPath ? `Output: ${outputPath}` : undefined,\n\t\titem.step?.sessionFile\n\t\t\t? `Session: ${item.step.sessionFile}`\n\t\t\t: item.run.sessionFile\n\t\t\t\t? `Session: ${item.run.sessionFile}`\n\t\t\t\t: undefined,\n\t\t\"\",\n\t\t\"Transcript\",\n\t\t\"(status is no longer available)\",\n\t].filter((line): line is string => line !== undefined);\n}\n\nfunction detailLines(item: FleetItem | undefined, error: string | undefined): string[] {\n\tif (!item)\n\t\treturn [\n\t\t\terror ? `Fleet scan failed: ${error}` : \"No current-session foreground or recent async children.\",\n\t\t\t\"\",\n\t\t\t\"New runs appear here automatically while this inspector remains open.\",\n\t\t];\n\tconst lines =\n\t\titem.kind === \"foreground-active\"\n\t\t\t? foregroundActiveDetail(item)\n\t\t\t: item.kind === \"foreground-recent\"\n\t\t\t\t? foregroundRecentDetail(item)\n\t\t\t\t: asyncDetail(item);\n\tif (error) lines.unshift(`Fleet scan warning: ${error}`, \"\");\n\treturn lines;\n}\n\nfunction isActionableAsyncState(state: string): boolean {\n\treturn state === \"running\" || state === \"queued\" || state === \"pending\";\n}\n\nfunction firstToolResultText(result: AgentToolResult<Details> | null, fallback: string): FleetActionResult {\n\tif (!result) return { text: fallback, isError: true };\n\tconst text = result.content.find((item) => item.type === \"text\")?.text ?? fallback;\n\treturn { text, ...(result.isError ? { isError: true } : {}) };\n}\n\nfunction uniquePaths(values: Array<string | undefined>): string[] {\n\treturn [...new Set(values.filter((value): value is string => Boolean(value)).map((value) => path.resolve(value)))];\n}\n\nfunction fleetArtifactsRoot(state: SubagentState, cwd: string): string {\n\treturn getArtifactsDir(state.parentSessionFile ?? null, cwd, state.artifactDirPreference ?? \"project\");\n}\n\nfunction transcriptTarget(item: FleetItem, state: SubagentState): { path: string; trustedRoots: string[] } | undefined {\n\tif (item.kind === \"foreground-active\") {\n\t\tconst artifactsRoot = fleetArtifactsRoot(state, item.control.cwd ?? state.baseCwd);\n\t\treturn {\n\t\t\tpath: getArtifactPaths(artifactsRoot, item.runId, item.agent, item.index ?? 0).transcriptPath,\n\t\t\ttrustedRoots: [artifactsRoot],\n\t\t};\n\t}\n\tif (item.kind === \"foreground-recent\") {\n\t\tif (!item.child.transcriptPath) return undefined;\n\t\tconst transcriptPath = path.isAbsolute(item.child.transcriptPath)\n\t\t\t? item.child.transcriptPath\n\t\t\t: path.resolve(item.run.cwd, item.child.transcriptPath);\n\t\treturn {\n\t\t\tpath: transcriptPath,\n\t\t\ttrustedRoots: uniquePaths([fleetArtifactsRoot(state, item.run.cwd), fleetArtifactsRoot(state, state.baseCwd)]),\n\t\t};\n\t}\n\tconst step = item.step ?? (item.run.steps.length === 1 ? item.run.steps[0] : undefined);\n\tif (!step?.transcriptPath) return undefined;\n\tconst transcriptPath = path.isAbsolute(step.transcriptPath)\n\t\t? step.transcriptPath\n\t\t: path.resolve(item.run.asyncDir, step.transcriptPath);\n\tconst trackedJob = state.fleetJobs?.get(item.runId) ?? state.asyncJobs.get(item.runId);\n\treturn {\n\t\tpath: transcriptPath,\n\t\ttrustedRoots: uniquePaths([\n\t\t\titem.run.asyncDir,\n\t\t\tfleetArtifactsRoot(state, state.baseCwd),\n\t\t\ttrackedJob?.cwd ? fleetArtifactsRoot(state, trackedJob.cwd) : undefined,\n\t\t]),\n\t};\n}\n\nfunction itemContext(item: FleetItem): string | undefined {\n\tif (item.kind === \"async\") return contextModeLabel(item.step?.context ?? item.run.context);\n\tif (item.kind === \"foreground-recent\") return contextModeLabel(item.child.context);\n\treturn undefined;\n}\n\nfunction itemMode(item: FleetItem): string {\n\treturn item.kind === \"foreground-active\" ? item.control.mode : item.run.mode;\n}\n\nfunction itemSource(item: FleetItem): string {\n\tif (item.kind === \"async\") return \"background\";\n\treturn item.kind === \"foreground-active\" ? \"foreground · live\" : \"foreground · recent\";\n}\n\nfunction itemStats(item: FleetItem): string[] {\n\tlet model: string | undefined;\n\tlet tokens: number | undefined;\n\tlet tools: number | undefined;\n\tlet durationMs: number | undefined;\n\tif (item.kind === \"foreground-active\") {\n\t\tconst live = item.activeChild ?? item.control;\n\t\tmodel = formatModelThinking(live.model, live.thinking) || undefined;\n\t\ttokens = live.tokens;\n\t\ttools = live.toolCount;\n\t\tdurationMs = Math.max(0, Date.now() - live.startedAt);\n\t} else if (item.kind === \"foreground-recent\") {\n\t\tmodel = formatModelThinking(item.child.model, item.child.thinking) || undefined;\n\t\ttokens = item.child.tokens;\n\t\ttools = item.child.toolCount;\n\t} else {\n\t\tmodel = formatModelThinking(item.step?.model, item.step?.thinking) || undefined;\n\t\ttokens = item.step?.tokens?.total ?? (item.index === undefined ? item.run.totalTokens?.total : undefined);\n\t\ttools = item.step?.toolCount ?? (item.index === undefined ? item.run.toolCount : undefined);\n\t\tconst terminalRun = item.state !== \"queued\" && item.state !== \"running\" && item.state !== \"pending\";\n\t\tconst endTime = item.run.endedAt ?? (terminalRun ? item.run.lastUpdate : undefined) ?? Date.now();\n\t\tdurationMs = item.step?.durationMs ?? Math.max(0, endTime - item.run.startedAt);\n\t}\n\treturn [\n\t\tmodel,\n\t\ttokens !== undefined ? `${formatTokens(tokens)} tok` : undefined,\n\t\ttools !== undefined ? `${tools} tool${tools === 1 ? \"\" : \"s\"}` : undefined,\n\t\tdurationMs !== undefined ? formatDuration(durationMs) : undefined,\n\t].filter((value): value is string => Boolean(value));\n}\n\nfunction structuredHeader(item: FleetItem, width: number, theme: Theme, conversationState: string): string[] {\n\tconst lines: string[] = [];\n\tlines.push(\n\t\trightAligned(` ${statusGlyph(item, theme)} ${theme.bold(item.agent)}`, theme.fg(\"dim\", item.state), width),\n\t);\n\tconst child = item.index !== undefined ? ` · child ${item.index + 1}` : \"\";\n\tconst context = itemContext(item);\n\tconst identity = `${itemSource(item)} · ${item.runId.slice(0, 8)}${child} · ${itemMode(item)}${context ? ` ${context}` : \"\"}`;\n\tlines.push(`  ${theme.fg(\"dim\", identity)}`);\n\tconst stats = itemStats(item);\n\tif (stats.length) lines.push(`  ${theme.fg(\"muted\", stats.join(\" · \"))}`);\n\tif (item.description) {\n\t\tconst task = item.description.replace(/\\s+/g, \" \").trim();\n\t\tlines.push(`  ${theme.fg(\"dim\", \"Task\")}  ${task}`);\n\t}\n\tlines.push(`${theme.fg(\"accent\", \"Conversation\")} ${theme.fg(\"dim\", `· ${conversationState}`)}`);\n\treturn lines.map((line) => truncateToWidth(line, width));\n}\n\nfunction fit(text: string, width: number): string {\n\tconst clipped = truncateToWidth(text, Math.max(0, width));\n\treturn clipped + \" \".repeat(Math.max(0, width - visibleWidth(clipped)));\n}\n\nfunction rightAligned(left: string, right: string, width: number): string {\n\tconst rightWidth = visibleWidth(right);\n\tconst leftWidth = Math.max(0, width - rightWidth - 1);\n\treturn fit(left, leftWidth) + \" \".repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);\n}\n\ninterface FleetDetailSections {\n\theader: string[];\n\tbody: string[];\n}\n\ninterface FleetTranscriptCache {\n\tpath: string;\n\tfingerprint: string;\n\twidth: number;\n\texpandedTools: boolean;\n\ttranscript: FleetTranscript;\n\tbody: string[];\n}\n\nfunction transcriptFingerprint(filePath: string): string {\n\ttry {\n\t\tconst stat = fs.statSync(filePath);\n\t\treturn `${stat.size}:${stat.mtimeMs}`;\n\t} catch {\n\t\treturn \"missing\";\n\t}\n}\n\nexport class SubagentFleetComponent implements Component {\n\tprivate snapshot: FleetSnapshot = { items: [] };\n\tprivate selected = 0;\n\tprivate selectedKey: string | undefined;\n\tprivate detailScroll = 0;\n\tprivate detailAutoFollow = true;\n\tprivate detailLineCount = 0;\n\tprivate detailViewportHeight = 8;\n\tprivate bodyHeight = 8;\n\tprivate expandedTools = false;\n\tprivate actionNotice: FleetActionResult | undefined;\n\tprivate steerDraft: string | undefined;\n\tprivate steerMode: SteerDeliveryMode = \"steer\";\n\tprivate stopConfirming = false;\n\tprivate actionBusy = false;\n\tprivate transcriptCache: FleetTranscriptCache | undefined;\n\tprivate disposed = false;\n\tprivate readonly timer: ReturnType<typeof setInterval>;\n\tprivate readonly tui: FleetTui;\n\tprivate readonly theme: Theme;\n\tprivate readonly markdownTheme: MarkdownTheme;\n\tprivate readonly state: SubagentState;\n\tprivate readonly done: (result: undefined) => void;\n\tprivate readonly options: FleetViewOptions;\n\n\tconstructor(\n\t\ttui: FleetTui,\n\t\ttheme: Theme,\n\t\tstate: SubagentState,\n\t\tdone: (result: undefined) => void,\n\t\toptions: FleetViewOptions = {},\n\t) {\n\t\tthis.tui = tui;\n\t\tthis.theme = theme;\n\t\tthis.markdownTheme = options.markdownTheme ?? getMarkdownTheme();\n\t\tthis.state = state;\n\t\tthis.done = done;\n\t\tthis.options = options;\n\t\tthis.selectedKey = options.initialKey;\n\t\tthis.refresh();\n\t\tthis.timer = setInterval(() => {\n\t\t\tif (this.disposed) return;\n\t\t\tthis.invalidate();\n\t\t\tthis.tui.requestRender();\n\t\t}, options.refreshMs ?? REFRESH_MS);\n\t\tthis.timer.unref?.();\n\t}\n\n\tprivate refresh(): void {\n\t\tconst previousKey = this.snapshot.items[this.selected]?.key ?? this.selectedKey;\n\t\tthis.snapshot = collectFleetSnapshot(this.state, this.options);\n\t\tconst preserved = previousKey ? this.snapshot.items.findIndex((item) => item.key === previousKey) : -1;\n\t\tthis.selected = preserved >= 0 ? preserved : Math.min(this.selected, Math.max(0, this.snapshot.items.length - 1));\n\t\tthis.selectedKey = this.snapshot.items[this.selected]?.key;\n\t}\n\n\tprivate moveSelection(delta: number): void {\n\t\tif (this.snapshot.items.length === 0) return;\n\t\tthis.selected = Math.max(0, Math.min(this.snapshot.items.length - 1, this.selected + delta));\n\t\tthis.selectedKey = this.snapshot.items[this.selected]?.key;\n\t\tthis.detailAutoFollow = true;\n\t\tthis.resetActionInput();\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate resetActionInput(): void {\n\t\tthis.steerDraft = undefined;\n\t\tthis.steerMode = \"steer\";\n\t\tthis.stopConfirming = false;\n\t}\n\n\tprivate selectedAsyncAction(): { item: Extract<FleetItem, { kind: \"async\" }> } | { reason: string } {\n\t\tconst item = this.snapshot.items[this.selected];\n\t\tif (!item) return { reason: \"No child is selected.\" };\n\t\tif (item.kind !== \"async\")\n\t\t\treturn { reason: \"Fleet controls are available for current-session top-level async runs only.\" };\n\t\tif (!isActionableAsyncState(item.run.state) || !isActionableAsyncState(item.state))\n\t\t\treturn { reason: `Selected child is ${item.state}; controls require a running or queued async child.` };\n\t\treturn { item };\n\t}\n\n\tprivate actionLines(): string[] {\n\t\tconst lines: string[] = [];\n\t\tif (this.actionBusy) lines.push(this.theme.fg(\"accent\", \"Action pending...\"));\n\t\tif (this.steerDraft !== undefined) {\n\t\t\tlines.push(\n\t\t\t\tthis.theme.fg(\n\t\t\t\t\t\"accent\",\n\t\t\t\t\t`Steer message (${this.steerMode}): ${this.steerDraft}${this.theme.fg(\"dim\", \"▌\")}`,\n\t\t\t\t),\n\t\t\t);\n\t\t\tlines.push(this.theme.fg(\"dim\", \"Enter sends · Tab changes mode · Esc cancels · Backspace edits\"));\n\t\t} else if (this.stopConfirming) {\n\t\t\tconst selected = this.snapshot.items[this.selected];\n\t\t\tlines.push(this.theme.fg(\"warning\", `Confirm stop for async run ${selected?.runId ?? \"selected run\"}?`));\n\t\t\tlines.push(\n\t\t\t\tthis.theme.fg(\n\t\t\t\t\t\"dim\",\n\t\t\t\t\t\"Stop ends the run; use interrupt for a resumable pause. Enter/Y confirms · N returns · Esc cancels\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (this.actionNotice) {\n\t\t\tlines.push(this.theme.fg(this.actionNotice.isError ? \"error\" : \"success\", this.actionNotice.text));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate withActionLines(body: string[]): string[] {\n\t\tconst actionLines = this.actionLines();\n\t\treturn actionLines.length ? [...actionLines, \"\", ...body] : body;\n\t}\n\n\tprivate setActionNotice(result: FleetActionResult): void {\n\t\tthis.actionNotice = result;\n\t\tthis.resetActionInput();\n\t\tthis.detailAutoFollow = false;\n\t\tthis.detailScroll = 0;\n\t\tthis.refresh();\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate runAction(action: () => Promise<FleetActionResult>): void {\n\t\tif (this.actionBusy) return;\n\t\tthis.actionBusy = true;\n\t\tthis.actionNotice = undefined;\n\t\tthis.tui.requestRender();\n\t\tvoid action()\n\t\t\t.then((result) => this.setActionNotice(result))\n\t\t\t.catch((error) =>\n\t\t\t\tthis.setActionNotice({ text: error instanceof Error ? error.message : String(error), isError: true }),\n\t\t\t)\n\t\t\t.finally(() => {\n\t\t\t\tthis.actionBusy = false;\n\t\t\t\tif (!this.disposed) this.tui.requestRender();\n\t\t\t});\n\t}\n\n\tprivate scrollDetail(delta: number): void {\n\t\tconst maxScroll = Math.max(0, this.detailLineCount - this.detailViewportHeight);\n\t\tthis.detailScroll = Math.max(0, Math.min(maxScroll, this.detailScroll + delta));\n\t\tthis.detailAutoFollow = this.detailScroll >= maxScroll;\n\t\tthis.tui.requestRender();\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (this.steerDraft !== undefined) {\n\t\t\tif (matchesKey(data, \"escape\") || matchesKey(data, \"ctrl+c\")) {\n\t\t\t\tthis.resetActionInput();\n\t\t\t\tthis.tui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matchesKey(data, \"return\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\t\tconst message = this.steerDraft.trim();\n\t\t\t\tif (!message) {\n\t\t\t\t\tthis.setActionNotice({ text: \"Steer message cannot be empty.\", isError: true });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst target = this.selectedAsyncAction();\n\t\t\t\tif (\"reason\" in target || !this.options.actions) {\n\t\t\t\t\tthis.setActionNotice({\n\t\t\t\t\t\ttext: \"reason\" in target ? target.reason : \"Fleet controls are unavailable in this context.\",\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.runAction(() =>\n\t\t\t\t\tthis.options.actions!.steer({\n\t\t\t\t\t\trunId: target.item.runId,\n\t\t\t\t\t\tasyncDir: target.item.run.asyncDir,\n\t\t\t\t\t\t...(target.item.index !== undefined ? { index: target.item.index } : {}),\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t\tmode: this.steerMode,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matchesKey(data, \"tab\") || data === \"\\t\") {\n\t\t\t\tconst modes: SteerDeliveryMode[] = [\"steer\", \"follow_up\", \"auto\"];\n\t\t\t\tthis.steerMode = modes[(modes.indexOf(this.steerMode) + 1) % modes.length]!;\n\t\t\t\tthis.tui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (matchesKey(data, \"backspace\") || data === \"\\x7f\") {\n\t\t\t\tthis.steerDraft = this.steerDraft.slice(0, -1);\n\t\t\t\tthis.tui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (data.length === 1 && data >= \" \" && data !== \"\\x7f\") {\n\t\t\t\tthis.steerDraft += data;\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (this.stopConfirming) {\n\t\t\tif (matchesKey(data, \"return\") || data.toLowerCase() === \"y\") {\n\t\t\t\tconst target = this.selectedAsyncAction();\n\t\t\t\tif (\"reason\" in target || !this.options.actions) {\n\t\t\t\t\tthis.setActionNotice({\n\t\t\t\t\t\ttext: \"reason\" in target ? target.reason : \"Fleet controls are unavailable in this context.\",\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.runAction(() =>\n\t\t\t\t\tPromise.resolve(\n\t\t\t\t\t\tthis.options.actions!.stop({\n\t\t\t\t\t\t\trunId: target.item.runId,\n\t\t\t\t\t\t\tasyncDir: target.item.run.asyncDir,\n\t\t\t\t\t\t\t...(target.item.index !== undefined ? { index: target.item.index } : {}),\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tmatchesKey(data, \"escape\") ||\n\t\t\t\tmatchesKey(data, \"ctrl+c\") ||\n\t\t\t\tdata.toLowerCase() === \"n\" ||\n\t\t\t\tmatchesKey(data, \"backspace\")\n\t\t\t) {\n\t\t\t\tthis.resetActionInput();\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"escape\") || matchesKey(data, \"ctrl+c\") || matchesKey(data, \"q\")) {\n\t\t\tthis.done(undefined);\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, Key.shift(\"k\"))) return this.scrollDetail(-1);\n\t\tif (matchesKey(data, Key.shift(\"j\"))) return this.scrollDetail(1);\n\t\tif (matchesKey(data, \"up\") || matchesKey(data, \"k\")) return this.moveSelection(-1);\n\t\tif (matchesKey(data, \"down\") || matchesKey(data, \"j\")) return this.moveSelection(1);\n\t\tif (matchesKey(data, \"home\")) return this.moveSelection(-this.snapshot.items.length);\n\t\tif (matchesKey(data, \"end\")) return this.moveSelection(this.snapshot.items.length);\n\t\tif (matchesKey(data, \"pageUp\")) return this.scrollDetail(-this.detailViewportHeight);\n\t\tif (matchesKey(data, \"pageDown\")) return this.scrollDetail(this.detailViewportHeight);\n\t\tif (data.toLowerCase() === \"r\") {\n\t\t\tthis.transcriptCache = undefined;\n\t\t\tthis.refresh();\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"s\") {\n\t\t\tconst target = this.selectedAsyncAction();\n\t\t\tif (\"reason\" in target || !this.options.actions)\n\t\t\t\tthis.setActionNotice({\n\t\t\t\t\ttext: \"reason\" in target ? target.reason : \"Fleet controls are unavailable in this context.\",\n\t\t\t\t\tisError: true,\n\t\t\t\t});\n\t\t\telse {\n\t\t\t\tthis.actionNotice = undefined;\n\t\t\t\tthis.steerDraft = \"\";\n\t\t\t\tthis.detailAutoFollow = false;\n\t\t\t\tthis.detailScroll = 0;\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"H\") {\n\t\t\tconst target = this.selectedAsyncAction();\n\t\t\tif (\"reason\" in target || !this.options.actions?.inspect)\n\t\t\t\tthis.setActionNotice({\n\t\t\t\t\ttext: \"reason\" in target ? target.reason : \"Herdr inspector controls are unavailable in this context.\",\n\t\t\t\t\tisError: true,\n\t\t\t\t});\n\t\t\telse\n\t\t\t\tthis.runAction(() =>\n\t\t\t\t\tthis.options.actions!.inspect!({\n\t\t\t\t\t\trunId: target.item.runId,\n\t\t\t\t\t\tasyncDir: target.item.run.asyncDir,\n\t\t\t\t\t\t...(target.item.index !== undefined ? { index: target.item.index } : {}),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"D\") {\n\t\t\tconst target = this.selectedAsyncAction();\n\t\t\tif (\"reason\" in target || !this.options.actions)\n\t\t\t\tthis.setActionNotice({\n\t\t\t\t\ttext: \"reason\" in target ? target.reason : \"Fleet controls are unavailable in this context.\",\n\t\t\t\t\tisError: true,\n\t\t\t\t});\n\t\t\telse {\n\t\t\t\tthis.actionNotice = undefined;\n\t\t\t\tthis.stopConfirming = true;\n\t\t\t\tthis.detailAutoFollow = false;\n\t\t\t\tthis.detailScroll = 0;\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (data.toLowerCase() === \"x\" || matchesKey(data, \"ctrl+o\")) {\n\t\t\tthis.expandedTools = !this.expandedTools;\n\t\t\tthis.transcriptCache = undefined;\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t}\n\n\tprivate rosterLines(width: number): string[] {\n\t\tif (this.snapshot.items.length === 0) return [this.theme.fg(\"dim\", \"No tracked children\")];\n\t\tconst start = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selected - this.bodyHeight + 1, Math.max(0, this.snapshot.items.length - this.bodyHeight)),\n\t\t);\n\t\treturn this.snapshot.items.slice(start, start + this.bodyHeight).map((item, offset) => {\n\t\t\tconst index = start + offset;\n\t\t\tconst marker = index === this.selected ? this.theme.fg(\"accent\", \"›\") : \" \";\n\t\t\tconst context =\n\t\t\t\titem.kind === \"async\"\n\t\t\t\t\t? contextModeBadge(this.theme, item.step?.context ?? item.run.context)\n\t\t\t\t\t: item.kind === \"foreground-recent\"\n\t\t\t\t\t\t? contextModeBadge(this.theme, item.child.context)\n\t\t\t\t\t\t: \"\";\n\t\t\tconst agent = index === this.selected ? this.theme.bold(item.agent) : item.agent;\n\t\t\tconst identity = item.description?.replace(/\\s+/g, \" \").trim() || item.runId.slice(0, 8);\n\t\t\tconst left = `${marker} ${statusGlyph(item, this.theme)} ${agent}${context} ${this.theme.fg(\"dim\", `· ${identity}`)}`;\n\t\t\treturn rightAligned(left, this.theme.fg(\"dim\", item.state), width);\n\t\t});\n\t}\n\n\tprivate renderedTranscript(\n\t\ttarget: { path: string; trustedRoots: string[] },\n\t\twidth: number,\n\t): { transcript: FleetTranscript; body: string[] } {\n\t\tconst fingerprint = `${target.trustedRoots.join(\"\\0\")}|${transcriptFingerprint(target.path)}`;\n\t\tif (\n\t\t\tthis.transcriptCache &&\n\t\t\tthis.transcriptCache.path === target.path &&\n\t\t\tthis.transcriptCache.fingerprint === fingerprint &&\n\t\t\tthis.transcriptCache.width === width &&\n\t\t\tthis.transcriptCache.expandedTools === this.expandedTools\n\t\t) {\n\t\t\treturn { transcript: this.transcriptCache.transcript, body: [...this.transcriptCache.body] };\n\t\t}\n\t\tconst transcript = readFleetTranscript(target.path, { trustedRoots: target.trustedRoots });\n\t\tconst body =\n\t\t\ttranscript.events.length > 0\n\t\t\t\t? renderFleetTranscript(transcript, width, this.theme, this.markdownTheme, {\n\t\t\t\t\t\texpandedTools: this.expandedTools,\n\t\t\t\t\t})\n\t\t\t\t: [];\n\t\tthis.transcriptCache = {\n\t\t\tpath: target.path,\n\t\t\tfingerprint,\n\t\t\twidth,\n\t\t\texpandedTools: this.expandedTools,\n\t\t\ttranscript,\n\t\t\tbody,\n\t\t};\n\t\treturn { transcript, body: [...body] };\n\t}\n\n\tprivate wrappedDetail(width: number): FleetDetailSections {\n\t\tconst selected = this.snapshot.items[this.selected];\n\t\tlet transcriptWarning: string | undefined;\n\t\tif (selected) {\n\t\t\tconst target = transcriptTarget(selected, this.state);\n\t\t\tif (target) {\n\t\t\t\tconst { transcript, body } = this.renderedTranscript(target, width);\n\t\t\t\ttranscriptWarning = transcript.warning;\n\t\t\t\tif (transcript.events.length > 0) {\n\t\t\t\t\tif (this.snapshot.error)\n\t\t\t\t\t\tbody.unshift(this.theme.fg(\"warning\", `Fleet scan warning: ${this.snapshot.error}`), \"\");\n\t\t\t\t\tconst latest = transcript.events.at(-1);\n\t\t\t\t\tconst conversationState =\n\t\t\t\t\t\tlatest?.kind === \"assistant\"\n\t\t\t\t\t\t\t? \"assistant response\"\n\t\t\t\t\t\t\t: latest?.kind === \"user\"\n\t\t\t\t\t\t\t\t? \"supervisor message\"\n\t\t\t\t\t\t\t\t: latest?.kind === \"tool\"\n\t\t\t\t\t\t\t\t\t? `${latest.name} · ${latest.status}`\n\t\t\t\t\t\t\t\t\t: \"activity\";\n\t\t\t\t\treturn {\n\t\t\t\t\t\theader: structuredHeader(selected, width, this.theme, conversationState),\n\t\t\t\t\t\tbody: this.withActionLines(body),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst raw = detailLines(selected, this.snapshot.error);\n\t\tif (transcriptWarning) raw.unshift(`Transcript preview warning: ${transcriptWarning}`, \"\");\n\t\tconst lines: string[] = [];\n\t\tfor (const line of raw) {\n\t\t\tconst styled = /^(Run|State|Mode|Source|Child|Agent|Model):/.test(line)\n\t\t\t\t? this.theme.bold(line)\n\t\t\t\t: /^(Transcript|Result transcript tail)/.test(line)\n\t\t\t\t\t? this.theme.fg(\"accent\", line)\n\t\t\t\t\t: /^(Output|Session|Transcript file|Artifacts):/.test(line)\n\t\t\t\t\t\t? this.theme.fg(\"muted\", line)\n\t\t\t\t\t\t: /^Transcript preview warning:/.test(line)\n\t\t\t\t\t\t\t? this.theme.fg(\"warning\", line)\n\t\t\t\t\t\t\t: line;\n\t\t\tconst wrapped = wrapTextWithAnsi(styled, Math.max(1, width));\n\t\t\tlines.push(...(wrapped.length ? wrapped : [\"\"]));\n\t\t}\n\t\treturn { header: [], body: this.withActionLines(lines) };\n\t}\n\n\trender(width: number): string[] {\n\t\tif (width < 36) return [truncateToWidth(\"Subagent fleet needs at least 36 columns. Esc closes.\", width)];\n\t\tconst innerWidth = width - 2;\n\t\tconst rows = this.tui.terminal?.rows ?? 32;\n\t\tthis.bodyHeight = Math.max(2, Math.floor(rows * 0.85) - 6);\n\t\tconst rosterWidth = Math.max(22, Math.min(46, Math.floor((innerWidth - 1) * 0.38)));\n\t\tconst detailWidth = Math.max(1, innerWidth - rosterWidth - 1);\n\t\tconst roster = this.rosterLines(rosterWidth);\n\t\tconst detail = this.wrappedDetail(detailWidth);\n\t\tconst detailHeader = detail.header.slice(0, Math.max(0, this.bodyHeight - 1));\n\t\tthis.detailViewportHeight = Math.max(1, this.bodyHeight - detailHeader.length);\n\t\tthis.detailLineCount = detail.body.length;\n\t\tconst maxDetailScroll = Math.max(0, detail.body.length - this.detailViewportHeight);\n\t\tif (this.detailAutoFollow) this.detailScroll = maxDetailScroll;\n\t\telse if (this.detailScroll > maxDetailScroll) this.detailScroll = maxDetailScroll;\n\t\tconst visibleDetails = [\n\t\t\t...detailHeader,\n\t\t\t...detail.body.slice(this.detailScroll, this.detailScroll + this.detailViewportHeight),\n\t\t];\n\t\tconst lines = [this.theme.fg(\"border\", `╭${\"─\".repeat(innerWidth)}╮`)];\n\t\tconst selected = this.snapshot.items[this.selected];\n\t\tconst title = ` ${this.theme.bold(\"Subagent fleet inspector\")} ${this.theme.fg(\"dim\", \"· live controls\")}`;\n\t\tconst selectedStatus = selected\n\t\t\t? `${statusGlyph(selected, this.theme)} ${selected.agent} · ${selected.state} `\n\t\t\t: this.theme.fg(\"dim\", \"no children \");\n\t\tlines.push(\n\t\t\tthis.theme.fg(\"border\", \"│\") + rightAligned(title, selectedStatus, innerWidth) + this.theme.fg(\"border\", \"│\"),\n\t\t);\n\t\tlines.push(this.theme.fg(\"border\", `├${\"─\".repeat(rosterWidth)}┬${\"─\".repeat(detailWidth)}┤`));\n\t\tfor (let index = 0; index < this.bodyHeight; index++) {\n\t\t\tlines.push(\n\t\t\t\tthis.theme.fg(\"border\", \"│\") +\n\t\t\t\t\tfit(roster[index] ?? \"\", rosterWidth) +\n\t\t\t\t\tthis.theme.fg(\"border\", \"│\") +\n\t\t\t\t\tfit(visibleDetails[index] ?? \"\", detailWidth) +\n\t\t\t\t\tthis.theme.fg(\"border\", \"│\"),\n\t\t\t);\n\t\t}\n\t\tlines.push(this.theme.fg(\"border\", `├${\"─\".repeat(rosterWidth)}┴${\"─\".repeat(detailWidth)}┤`));\n\t\tconst position = this.snapshot.items.length ? `${this.selected + 1}/${this.snapshot.items.length}` : \"0/0\";\n\t\tconst footer = ` ↑↓/jk agent · H Herdr · s steer · D stop · x/Ctrl+O tools · r refresh · Esc close · ${position}`;\n\t\tlines.push(\n\t\t\tthis.theme.fg(\"border\", \"│\") + fit(this.theme.fg(\"dim\", footer), innerWidth) + this.theme.fg(\"border\", \"│\"),\n\t\t);\n\t\tlines.push(this.theme.fg(\"border\", `╰${\"─\".repeat(innerWidth)}╯`));\n\t\treturn lines.map((line) => truncateToWidth(line, width));\n\t}\n\n\tinvalidate(): void {\n\t\tthis.transcriptCache = undefined;\n\t\tthis.refresh();\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tclearInterval(this.timer);\n\t}\n}\n\nexport async function openSubagentFleet(\n\tctx: ExtensionContext,\n\tstate: SubagentState,\n\toptions: FleetViewOptions = {},\n): Promise<void> {\n\tconst wasOpen = state.fleetInspectorOpen === true;\n\tstate.fleetInspectorOpen = true;\n\tif (typeof ctx.ui.setWidget === \"function\") ctx.ui.setWidget(FLEET_STATUS_WIDGET_KEY, undefined);\n\tconst actions =\n\t\toptions.actions ??\n\t\t({\n\t\t\tsteer: async (input: {\n\t\t\t\trunId: string;\n\t\t\t\tasyncDir: string;\n\t\t\t\tindex?: number;\n\t\t\t\tmessage: string;\n\t\t\t\tmode: SteerDeliveryMode;\n\t\t\t}) =>\n\t\t\t\tfirstToolResultText(\n\t\t\t\t\tawait steerAsyncRun({\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\trunId: input.runId,\n\t\t\t\t\t\t...(input.index !== undefined ? { index: input.index } : {}),\n\t\t\t\t\t\tmessage: input.message,\n\t\t\t\t\t\tmode: input.mode,\n\t\t\t\t\t\tlocation: { asyncDir: input.asyncDir, resolvedId: input.runId } as Parameters<\n\t\t\t\t\t\t\ttypeof steerAsyncRun\n\t\t\t\t\t\t>[0][\"location\"],\n\t\t\t\t\t}),\n\t\t\t\t\t`Failed to steer async run ${input.runId}.`,\n\t\t\t\t),\n\t\t\tstop: (input: { runId: string; asyncDir: string; index?: number }) =>\n\t\t\t\tfirstToolResultText(\n\t\t\t\t\tstopAsyncRun(state, input.runId, undefined, { asyncDir: input.asyncDir, resolvedId: input.runId }),\n\t\t\t\t\t`Failed to stop async run ${input.runId}.`,\n\t\t\t\t),\n\t\t\tinspect: async (input: { runId: string; asyncDir: string; index?: number }) =>\n\t\t\t\tfirstToolResultText(\n\t\t\t\t\tawait handleHerdrInspectorAction(\n\t\t\t\t\t\t\"inspector.open\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: input.runId,\n\t\t\t\t\t\t\tdir: input.asyncDir,\n\t\t\t\t\t\t\t...(input.index !== undefined ? { index: input.index } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t\tcwd: state.baseCwd,\n\t\t\t\t\t\t\t...(state.authorityPolicy ? { authorityPolicy: state.authorityPolicy } : {}),\n\t\t\t\t\t\t\t...(state.missionStoreConfig ? { missions: state.missionStoreConfig } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t\t`Failed to open Herdr inspector for async run ${input.runId}.`,\n\t\t\t\t),\n\t\t} satisfies FleetActionHandlers);\n\ttry {\n\t\tawait ctx.ui.custom<undefined>(\n\t\t\t(tui, theme, _keybindings, done) =>\n\t\t\t\tnew SubagentFleetComponent(tui, theme, state, done, { ...options, actions }),\n\t\t\t{\n\t\t\t\toverlay: true,\n\t\t\t\toverlayOptions: { anchor: \"center\", width: \"95%\", minWidth: 60, maxHeight: \"85%\", margin: 1 },\n\t\t\t},\n\t\t);\n\t} finally {\n\t\tstate.fleetInspectorOpen = wasOpen;\n\t}\n}\n"]}