/** * Live subagent activity feed: a bounded, display-only projection of CHILD * session events. The transcript store folds only the root session (the * durable truth this TUI renders); subagent conversations are their own * sessions, and before this module their events were dropped entirely — * a running subagent was invisible until its parent tool call settled. * * This is NOT a second transcript: each child folds to ONE row (label, * running state, bounded last-activity text), capped at * {@link MAX_SUBAGENT_ROWS}. The cap is a display budget, not a fan-out * limit: a new running child evicts the OLDEST settled row when one * exists, and while every row is busy the newcomer waits off-screen — but * the observed-session total (getTotalSeen) keeps counting, so status * totals never under-report the fan-out. Rows are advisory display * state, rebuilt from live events; nothing here persists or replays. * Notification is coalesced * by the same ~16ms frame throttle as the transcript store (per-burst * microtask notify chained SyncLane rerenders past React's nested update * limit; a bare macrotask merge repaints a whole turn's bursts at once). * * @module @deepseek-ai/dsh-code/session/subagents */ import type { SessionEvent } from '@deepseek-ai/dsh-session'; /** Hard row cap: overflow evicts the oldest settled row; a fully busy feed waits. */ export declare const MAX_SUBAGENT_ROWS = 8; /** One live subagent row in the feed. */ export interface SubagentRow { /** Child session id. */ readonly id: string; /** Display label (session title when observed, else a short id form). */ readonly label: string; /** Coarse lifecycle state folded from the child's events. */ readonly state: 'running' | 'idle' | 'done'; /** Bounded last-activity text for the status line. */ readonly activity: string; /** Last fold time (ms, event clock) — newest-first ordering key. */ readonly updatedAt: number; } /** The read-only snapshot surface the renderer subscribes to. */ export interface SubagentFeedView { /** Subscribe to feed changes; returns the unsubscribe function. */ subscribe(listener: () => void): () => void; /** Read the current rows (identity-stable between changes). */ getSnapshot(): readonly SubagentRow[]; /** * Distinct child sessions observed since the last reset. The row cap is * a display budget, not a count of the fan-out; totals surface this. */ getTotalSeen(): number; } /** * Fold one child-session event into its feed row (pure). * Unknown event kinds leave the row untouched. * @param previous - the row's current state, when any. * @param sessionId - the child session id. * @param event - the child session event. * @returns the next row state. */ export declare function foldSubagentRow(previous: SubagentRow | undefined, sessionId: string, event: SessionEvent): SubagentRow; /** * Create one subagent feed. `apply` folds a child event (the caller gates * which sessions are children); `reset` clears on a session switch. Row * order is first-seen; the snapshot array is frozen and only replaced when * a row actually changed. * @returns the mutable feed handle plus its `SubagentFeedView`. */ export declare function createSubagentFeed(): SubagentFeedView & { apply(sessionId: string, event: SessionEvent): void; reset(): void; }; /** * Root-log catalog facts a resumed session must replay into the subagent * feed: constructor seeds never fire on the live bus, so without this the * children of a resumed session vanish behind a restart. The empty-child * placeholder row (childId '') is a placeholder, not a child, and stays out. */ export declare function subagentCatalogSeed(events: readonly SessionEvent[]): readonly SessionEvent<'subagent/catalog'>[];