import type { ContentMessageItemImage, ContentMessageItemResource, ContentMessageItemText, RecordFrameToolInput, TimelineEventCategory, TimelineRecording } from "#ai-utils"; import type { ReplayActionUploader } from "./replay-action-uploader"; interface TimelineCollectorOptions { framesDir: string; debug?: boolean; enableCdnUpload?: boolean; } interface CollectedFrame { image: ContentMessageItemImage; title: string; category: TimelineEventCategory; eventId: number; explicit: boolean; timestamp: number; fileName: string; imageUrl?: string; uploadPromise?: Promise; cursorX?: number; cursorY?: number; viewportWidth?: number; viewportHeight?: number; } interface ProgressSnapshot { eventCount: number; frameCount: number; lastLabel: string; lastFrameFileName?: string; lastThinking?: string; } export interface StateEntry { ts: number; agentId: string; kind: "test_outcome"; status?: "accepted" | "rejected"; reason?: string; test_case_id?: string; [key: string]: any; } export declare class TimelineCollector { #private; constructor(sessionId: string, options: TimelineCollectorOptions); setReplayUploader(uploader: ReplayActionUploader): void; /** * Reset all collected state so the collector can be reused for a new recording cycle * (e.g., on reused QA branches that handle multiple review messages). */ reset(): void; onToolCall(name: string, input: string): void; /** Stage the latest screenshot image so the paired RecordFrame can capture it. */ onToolResult(result: { tool_name?: string; content: string | (ContentMessageItemText | ContentMessageItemImage | ContentMessageItemResource)[]; }): void; onRecordFrame(input: RecordFrameToolInput): void; onThinking(content: string): void; getLastCapturedImage(): ContentMessageItemImage | null; getExplicitFrames(): Array<{ image: ContentMessageItemImage | null; title: string; }>; getExplicitImages(): ContentMessageItemImage[]; getAllImages(): ContentMessageItemImage[]; getLegacyMetadataFrames(): Array<{ hasImage: boolean; title: string; }>; getExplicitFrameDurations(): string[]; getProgressSnapshot(): ProgressSnapshot; getEventCount(): number; getFrameCount(): number; getLastLabel(): string; hasExplicitFrames(): boolean; hasAnyFrames(): boolean; /** * Merge all events and frames from a child agent's timeline into this one. * Used to combine parallel executor timelines into a single planner recording. * Adds section markers and prefixes labels with the phase name. */ mergeChildTimeline(child: TimelineCollector, label: string, opts?: { summary?: string; sessionId?: string; testCaseIds?: string[]; replayId?: string; }): { startEventId: number; endEventId: number; }; /** Merge a child executor's test plan into this timeline for recording submission. */ mergeTestPlan(plan: { mode: string; test_cases: Array<{ id: string; description?: string; title?: string; expected_outcome: string; priority: string; }>; }): void; /** Returns the merged test plan from child executors, if any. */ getMergedTestPlan(): { mode: string; test_cases: Array<{ id: string; description?: string; title?: string; expected_outcome: string; priority: string; }>; } | null; /** Merge child executor test case results into this timeline. Last write wins by test_case_id. */ mergeTestCaseResults(results: Array>): void; /** Returns all merged test case results from child executors. */ getMergedTestCaseResults(): Record[]; /** * Path to this collector's append-only state file. One JSONL line per * `ReportTestOutcome` call (accepted or rejected) */ getStateFilePath(): string; /** Record a test outcome (accepted or rejected) to the state file. */ onTestOutcome(input: Record, status: "accepted" | "rejected", reason?: string): void; /** * Merge a child agent's state file into this one. Called from * mergeChildTimeline so parent's finalize() sees both own and child state. */ mergeChildStateFile(childPath: string): void; /** * Read all state entries from disk. Used by finalize-from-disk on * crash recovery. Malformed lines are skipped silently. */ readState(): StateEntry[]; getHighlightFrames(): CollectedFrame[]; getHighlightImages(): ContentMessageItemImage[]; getHighlightMetadataFrames(): Array<{ hasImage: boolean; title: string; }>; getHighlightFrameDurations(): string[]; /** * Removes all local frame files and metadata for this session. * Safe to call after timeline has been persisted to the backend (frames are on CDN). */ cleanupSessionFiles(): Promise; finalize(): Promise; } export {};