/** * agent-dashboard.ts — Rich interactive TUI dashboard for subagent management. * * Performance architecture (v2): * - Adaptive refresh: 200ms when agents are running, 750ms when idle. * - Spinner animation: advanced on every timer tick, not just user input. * - Dirty flag: tracks structural changes (agent IDs, statuses) to skip * expensive state recomputation when only the spinner ticked. * - Memoized theme/box chars: cached until UI style changes. * - Coalesced debounce: multiple rapid spawn requests batched with 16ms cap. * - Agent snapshot: lightweight structural hash for change detection. */ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import type { AgentManager } from "../agent-manager.js"; import type { SubagentScheduler } from "../schedule.js"; import type { AgentRecord } from "../types.js"; import type { AgentActivity } from "./agent-ui-types.js"; import { type RenderMetricsSnapshot } from "./render-metrics.js"; import { type Component, type TUI } from "./tui-shim.js"; export declare const DASHBOARD_HEIGHT_PCT = 100; export interface AgentDashboardOptions { manager: AgentManager; agentActivity: Map; scheduler?: SubagentScheduler; onViewConversation?: (record: AgentRecord) => Promise; onAbort?: (id: string) => boolean; onSteer?: (id: string) => Promise; onShowPermissions?: (record: AgentRecord) => Promise; onSwarmAction?: (action: string, agentIds: string[]) => Promise; } export declare class AgentDashboard implements Component { private readonly tui; private readonly options; private readonly done; private scrollOffset; private selectedIndex; private closed; private agents; private spinnerFrame; private bodyLineCount; private bodyFocusLineByAgentId; /** Multi-select support */ private selectedIds; private showHelp; private showPerf; /** Which metrics view to show: dashboard or widget. */ private perfView; private inCommandMode; private commandBuffer; private topViewMode; private topSortKey; private topSortAsc; private topPage; private topPageSize; private showSchedules; private showTree; /** Scroll offset for scrollable overlays (help, tree, schedules, perf). */ private subViewScrollOffset; private refreshTimer; /** Debounce state: true while a microtask render is pending. */ private renderPending; /** Timestamp of the last actual render for rate limiting. */ private lastRenderTime; /** Debounce timer handle for coalescing multiple rapid state changes. */ private coalesceTimer; /** Cached theme colors — recomputed only when UI style changes. */ private cachedTheme; /** Cached box chars — recomputed only when UI style changes. */ private cachedBoxChars; /** Last known UI style for cache invalidation. */ private lastUiStyle; /** * Lightweight structural snapshot: numeric hash of agent IDs + statuses. * Uses FNV-1a inspired hashing for O(1) comparison instead of O(N) string equality. */ private agentSnapshotHash; /** * Cached body lines — only rebuilt when dirty flag is set. * Prevents expensive rebuildDashboardBodyLines() on spinner-only ticks. */ private cachedBodyLines; private cachedBodyFocusMap; private cachedBodyLineCount; private cachedSpinnerFrame; private cachedInnerW; /** * True when the snapshot changed during the last refreshAgents() call. * Reset after each render. Used to decide whether full recompute is needed. */ private dirty; /** Render timing tracker for monitoring dashboard render() performance. */ private renderMetrics; /** Timestamp of first spawned agent (for time-to-first-visible). */ private firstSpawnedAt; constructor(tui: TUI, options: AgentDashboardOptions, done: (result: undefined) => void); /** * Compute the optimal refresh interval based on current agent state. * - 100+ agents: TURBO_REFRESH_MS (100ms) — high throughput, rapid state changes * - 50-99 agents: HIGH_LOAD_REFRESH_MS (150ms) — balanced for medium-large lists * - Running/queued agents: ACTIVE_REFRESH_MS (200ms) — captures state transitions * - Idle with < 50 agents: getDashboardRefreshInterval() (750ms) — low overhead */ private computeRefreshInterval; /** * Start (or restart) the refresh timer with an adaptive interval. * Interval adapts to agent count and activity level. */ private startRefreshTimer; private hasRunningAgents; /** * Coalesced render request with rate limiting. * * - First call in a burst: schedules via queueMicrotask (fires after current * synchronous work completes, batching all concurrent spawns). * - Subsequent calls within 16ms of the last render: coalesced via setTimeout * so we never render more than ~60 fps. * - Duplicate calls while pending: ignored (renderPending guard). */ private requestRender; private close; /** * Refresh agent list and detect structural changes. * Sets `this.dirty` when agent IDs or statuses changed. */ private refreshAgents; /** Get theme colors, recomputing only when UI style changes. */ private getTheme; /** Get box characters, recomputing only when UI style changes. */ private getBox; private getWidgetMetrics; private executeCommand; private statusMessage; private statusMessageTimer; /** Show a temporary status message in the dashboard footer. */ private showStatus; handleInput(data: string): void; private getViewportHeight; /** * Dynamically compute chrome lines based on terminal height. * Small terminals get proportionally less chrome; large terminals get more. * - Very small (< 30 rows): 10 lines (minimal chrome) * - Small (30-50 rows): 13 lines * - Normal (50-80 rows): 16 lines * - Large (> 80 rows): 19 lines */ private chromeLines; private keepSelectedBodyLineVisible; private renderState; render(width: number): string[]; /** Get render performance metrics snapshot. */ getRenderMetrics(): RenderMetricsSnapshot; invalidate(): void; dispose(): void; } /** * Launch the interactive agent dashboard as an overlay. */ export declare function showAgentDashboard(ctx: ExtensionCommandContext, manager: AgentManager, agentActivity: Map, scheduler?: SubagentScheduler, onViewConversation?: (record: AgentRecord) => Promise, onAbort?: (id: string) => boolean, onSteer?: (id: string) => Promise, onShowPermissions?: (record: AgentRecord) => Promise, onSwarmAction?: (action: string, agentIds: string[]) => Promise): Promise;