import { type AutomaticAnalysisHooks, type AutomaticAnalysisResult } from "./automatic-analysis-completion.js"; import { type RunLabProvenance } from "./run-status.js"; import type { ActorCompletionReason, ActorPersonaRef, ActorStatus } from "./actor-contract.js"; import { type E2BDesktopModule, type E2BDesktopSandbox } from "./e2b-desktop-launch.js"; import type { DetachedTimers } from "./e2b-detached.js"; import type { LabConfig } from "./lab-config.js"; import { renderObserver, type ObserverResult } from "./observer.js"; import { type RunBundle, type RunSubjectProvenance } from "./run.js"; import { type BrowserPersonaJourney, type BrowserSurface, type ScriptedBrowserLaunchArgs, type ScriptedBrowserLike, type ScriptedBrowserSessionOptions, type ScriptedBrowserSessionResult } from "./scripted-browser-actor.js"; export declare const SCRIPTED_BROWSER_LAB_SCHEMA = "humanish.scripted-lab-result.v1"; /** * Library-level hooks: DI seams so CI drives the full path (real engine, real projection) * with a fake browser at zero spend, plus the production browser resolution override. */ export interface ScriptedBrowserLabHooks { runSession?: (options: ScriptedBrowserSessionOptions) => Promise; /** Injected browser factory — forwarded to every session; skips browser-binary resolution. */ launchBrowser?: (args: ScriptedBrowserLaunchArgs) => Promise; /** Test/library env seam; CLI passes process.env. Values are scrubbed, names only persist. */ env?: Record; /** E2B DI seam for clone × e2b-desktop × scripted-browser. */ loadDesktopModule?: () => Promise; /** Optional adopter hook after subject sandbox creation, before clone provisioning. */ prepareDesktop?: (desktop: E2BDesktopSandbox) => Promise; /** Detached-step timers for deterministic tests around clone/seed/start provisioning. */ detachedTimers?: DetachedTimers; /** Override the resolved browser binary (tests; operators use HUMANISH_BROWSER_COMMAND). */ browserCommand?: string; renderObserverFn?: typeof renderObserver; now?: () => number; } export interface RunScriptedBrowserLabOptions { automaticAnalysis?: AutomaticAnalysisHooks; /** Which manifest produced this run (#455); threaded into the status record + bundle. */ lab?: RunLabProvenance; cwd: string; config: LabConfig; /** Resolved upstream (scenario.mode + CLI override); defaults safe (dry-run). */ dryRun: boolean; open?: boolean; runId?: string; hooks?: ScriptedBrowserLabHooks; } export interface ScriptedBrowserLabSession { surface: string; status: ActorStatus; completionReason: ActorCompletionReason; reason: string; screenshots: number; } export interface ScriptedBrowserLabResult extends AutomaticAnalysisResult { schema: typeof SCRIPTED_BROWSER_LAB_SCHEMA; /** True when the bundle verified AND (dry-run, or every session reached a terminal verdict * without a harness error). The subject failing the script is successful EVIDENCE, not a lab * failure — deliberate divergence from `run --app-url`, whose ok means "journey passed". */ ok: boolean; cwd: string; labId: string; /** The registry-resolved actor id that ran (or would run) the sessions. */ actor: string; appUrl: string; dryRun: boolean; runId: string; subject?: RunSubjectProvenance; subjectSandbox?: { sandboxId: string; killed: boolean; }; hostDigest?: string; /** The consumed scenario.ref: digest-pinned provenance of the executable steps. */ scenario?: { id: string; source: string; sourceDigest: string; steps: number; }; sessions: ScriptedBrowserLabSession[]; observer?: ObserverResult; warnings: string[]; error?: { code: "HUMANISH_LAB_ANALYSIS_INVALID" | "HUMANISH_LAB_TASKS_UNSUPPORTED" | "HUMANISH_SCRIPTED_LAB_FAILED" | "HUMANISH_SCRIPTED_LAB_ACTOR_UNSUPPORTED" | "HUMANISH_SCRIPTED_LAB_SCENARIO_INVALID" | "HUMANISH_SCRIPTED_LAB_SUBJECT_UNSAFE" | "HUMANISH_SCRIPTED_LAB_BROWSER_MISSING" | "HUMANISH_SCRIPTED_LAB_KEYS_MISSING" | "HUMANISH_SCRIPTED_LAB_SUBJECT_ENV_MISSING" | "HUMANISH_SCRIPTED_LAB_GETHOST_UNAVAILABLE"; message: string; }; } /** * Wrapped so a DIRECT library caller gets the same status-record lifetime the CLI does: returning * from this function finalizes any record the run opened, whichever of its fail-closed exits it * took. `runLab` establishes a scope too and nesting is harmless — the inner scope owns what it * opened. Without this a test or an adopter calling the backend directly leaves the 5s cadence * ticking into a directory something else is deleting, which surfaces as an unrelated ENOTEMPTY. */ export declare function runScriptedBrowserLab(options: RunScriptedBrowserLabOptions): Promise; /** * Project the scripted lab run into a humanish.run-bundle.v1 (no schema change — a new * producer only). The load-bearing line is `stream.actor = result.trace`: the provider-neutral * ActorTrace seam the Observer renders and verifyRun's engagement check reads. Exported for * the bundle-builder tests. */ export declare function buildScriptedLabBundle(args: { /** Lab provenance for the bundle\'s own `lab` field (#455). */ lab?: RunLabProvenance; actorId: string; appUrl: string; createdAt: string; desktopTemplate?: string; dryRun: boolean; hostDigest?: string; journey: BrowserPersonaJourney; labId: string; labTitle?: string; persona: ActorPersonaRef; runId: string; scenarioSource: string; scenarioSourceDigest: string; screenshotsBySurface: Map; sessionResults: ScriptedBrowserSessionResult[]; sessionError?: string; source: RunBundle["source"]; subject?: RunSubjectProvenance; surfaces: BrowserSurface[]; }): RunBundle;