/** * [WHO]: Provides StreamRenderController + StreamRenderContext (state/layout/loaders/toolTrace/runtime/ * escape/surface ports) — the interactive streaming render layer (handleEvent) * [FROM]: Depends on interactive-state (shared render fields), tui (Container/Text/Spacer/TUI/MarkdownTheme), * components (Assistant/Tool/CatuiLoader), theme, agent-session (AgentSessionEvent), ai (Message), * agent-core (AgentMessage), extensions-host (ToolDefinition), buddy pet-sprites (BuddyState) * [TO]: Consumed by modes/interactive/interactive-mode.ts (held as `this.streamRender`; subscribeToAgent's * handleEvent forwards here) * [HERE]: modes/interactive/controllers/stream-render-controller.ts — P5 UI04 render-layer slice (scope A) * * Owns the AgentSession event → TUI render orchestration: run lifecycle/loader, assistant streaming, * user/custom echo, tool-execution display, and the auto-compaction / auto-retry overlays. It is a render * layer: it reads session events and writes components, and NEVER submits any message to AgentSession * (token-neutral). Shared render state stays in the consolidated `interactive-state` holder (accessed via * the `state` port — that holder's stated purpose); the auto-compaction / auto-retry loader+escape state * (zero external readers) is owned privately here. The escape override during compaction/retry goes through * the `escape` port — the single controlled channel onto `defaultEditor.onEscape` shared with * InterruptController (whose dispatchEscape closure is what gets saved and restored). * See ../../../.dev-docs/architecture-review/interactive-ui-review/handle-event-analysis.md */ import type { AgentMessage } from "@catui/agent-core"; import type { Message } from "@catui/ai/types"; import { type CachedContainer, type Container, type MarkdownTheme, type TUI } from "@catui/tui"; import type { ToolDefinition } from "../../../core/extensions-host/types.js"; import type { AgentSessionEvent } from "../../../core/runtime/agent-session.js"; import type { BuddyState } from "../components/buddy/pet-sprites.js"; import type { InteractiveState } from "../state/interactive-state.js"; export interface StreamRenderStatePort { get(): InteractiveState; } export interface StreamRenderLayoutPort { getUi(): TUI; getChatContainer(): CachedContainer; getStatusContainer(): Container; addMessageToChat(message: AgentMessage): void; updatePendingMessagesDisplay(): void; rebuildChatFromMessages(): void; requestRender(): void; invalidateFooter(): void; } export interface StreamRenderLoadersPort { getSessionId(): string; getDefaultWorkingMessage(): string; /** Localized " to cancel" hint key for the interrupt action. */ getInterruptKeyHint(): string; setBuddyPetState(state: BuddyState, speechBubble?: string, options?: { resetTo?: BuddyState; afterMs?: number; }): void; startAgentRunTimer(): void; stopAgentRunTimer(): void; updateWorkingMessage(options?: { resetStallTimer?: boolean; }): void; formatElapsedSeconds(ms: number): string; isInPlanMode(): boolean; } export interface StreamRenderToolTracePort { shouldRenderToolTrace(toolName: string): boolean; getRegisteredToolDefinition(toolName: string): ToolDefinition | undefined; getShowImages(): boolean; } export interface StreamRenderRuntimePort { getRetryAttempt(): number; abortCompaction(): void; abortRetry(): void; flushCompactionQueue(options: { willRetry: boolean; }): void; checkShutdownRequested(): Promise; clearAttachments(): void; getAgentDir(): string; } /** The single controlled channel onto defaultEditor.onEscape, shared with InterruptController. */ export interface StreamRenderEscapePort { getHandler(): (() => void) | undefined; setHandler(handler: (() => void) | undefined): void; } export interface StreamRenderSurfacePort { ensureInitialized(): Promise; restoreEditorFocusIfPossible(): void; getUserMessageText(message: Message): string; getMarkdownThemeWithSettings(): MarkdownTheme; showStatus(message: string): void; showError(message: string): void; } export interface StreamRenderContext { state: StreamRenderStatePort; layout: StreamRenderLayoutPort; loaders: StreamRenderLoadersPort; toolTrace: StreamRenderToolTracePort; runtime: StreamRenderRuntimePort; escape: StreamRenderEscapePort; surface: StreamRenderSurfacePort; } export declare class StreamRenderController { private readonly ctx; private autoCompactionLoader; private autoCompactionEscapeHandler; private retryLoader; private retryEscapeHandler; private taskUpdateUnsubscribe; private taskAutoHideTimer; private hiddenCompletedTaskSignature; constructor(ctx: StreamRenderContext); handle(event: AgentSessionEvent): Promise; /** Task tool names that should trigger a panel refresh. */ private static readonly TASK_TOOL_NAMES; /** Refresh the task status panel from disk. */ private refreshTaskPanel; }