@builder.io/dev-tools
Version:
Builder.io Visual CMS Devtools
146 lines (145 loc) • 5.24 kB
TypeScript
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<void>;
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<Record<string, any>>): void;
/** Returns all merged test case results from child executors. */
getMergedTestCaseResults(): Record<string, any>[];
/**
* 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<string, any>, 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<void>;
finalize(): Promise<TimelineRecording>;
}
export {};