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 BrowserLabAdapterHooks } from "./adapter-extension.js"; import { type CuaActorDescriptor } from "./actor-registry.js"; import type { CuaActorSessionOptions } from "./computer-use-actor.js"; import type { CuaLoopResult } from "./computer-use.js"; import type { ReasoningEffort } from "./reasoning-effort.js"; import { type DesktopBrowserEvidence, type SubjectPhaseEvent } from "./cua-actor-lab.js"; import { type E2BDesktopModule, type E2BDesktopSandbox } from "./e2b-desktop-launch.js"; import { type DetachedTimers } from "./e2b-detached.js"; import type { DevicePreset } from "./device-presets.js"; import { type LabConfig, type LabSubjectStateCheckpoint } from "./lab-config.js"; import { renderObserver, type ObserverResult } from "./observer.js"; import type { LocalTreeArchive } from "./source-archive.js"; import type { DwellWindow, StopWhen } from "./stop-conditions.js"; import { type RunBundle, type RunDesktopGeometry, type RunScorerProvenance, type RunStream, type RunSubjectProvenance, type SharedWorldCheckpoint } from "./run.js"; export declare const SHARED_WORLD_LAB_SCHEMA = "humanish.shared-world-lab-result.v1"; export declare const SHARED_WORLD_LAB_PROVIDER_METADATA: { readonly mode: "shared-world-lab"; readonly tool: "humanish"; }; /** * Library-level hooks mirroring CuaActorLabHooks — the DI seams that let CI drive the FULL * orchestration with fakes at $0/zero-network. The fake desktop module records create/kill BY id * and exposes NO `list` method (the by-id teardown rail is then provable by construction). */ export interface SharedWorldLabHooks extends BrowserLabAdapterHooks { /** Lazy-load the E2B desktop module (tests inject a fake; default loadE2BDesktopModule). */ loadDesktopModule?: () => Promise; /** Runs once after sandbox creation, before subject provisioning (library setup seam). */ prepareDesktop?: (desktop: E2BDesktopSandbox) => Promise; /** The per-seat computer-use session runner (default: the resolved actor descriptor's). */ runSession?: (options: CuaActorSessionOptions) => Promise; /** The operator environment (keys + subject env values). Defaults to process.env. */ env?: Record; renderObserverFn?: typeof renderObserver; /** Injected clock/sleep for the detached-step polling (tests only). */ detachedTimers?: DetachedTimers; /** * Subject-provisioning phase sink (mirrors CuaActorLabHooks.onPhase): one call per * started/completed boundary during the ONE shared-plane provision (clone route: clone, install, * build, serve start, ready, subject.state seed-step groups; local-tree route: upload, extract, * install, build, serve start, ready, seed-step groups - no clone phase). Defaults to one stderr * line per event. Override in tests to capture instead of writing to real stderr. */ onPhase?: (event: SubjectPhaseEvent) => void; /** * CONCURRENT route only (#164 phase 2): the harness clock used to MEASURE each actor's laneWindow * [start,end] (default Date.now). The deterministic heart test does NOT override this — overlap is * produced by a rendezvous latch in the fake runSession + measured by the REAL clock (FIX-1), so * the windows are real, not injected. (A test may override only for non-overlap assertions.) */ now?: () => number; /** CONCURRENT route only: the background stateSeries prober cadence (ms). Default 1000. */ proberCadenceMs?: number; /** * EXTERNAL-PUBLIC concurrent route only (#164 phase 2): the host-first handoff barrier deadline * (ms). The host seat must surface a shared-session (/lobby/CODE) URL within this budget or the run * fails closed with HUMANISH_CONCURRENT_SHARED_WORLD_LAB_HANDOFF_TIMEOUT and no follower opens. * Default 120000 (also capped by execution.timeoutMs). Tests inject a short value to exercise the * fail-closed path deterministically. */ handoffDeadlineMs?: number; /** * EXTERNAL-PUBLIC concurrent route only: the vision reader that extracts a /lobby/CODE off a seat's * screenshot frame (the CDP-independent handoff relay + per-seat convergence observation). Defaults to * the real single-frame OpenAI read (readLobbyCodeFromFrame). Tests inject a fake so the barrier's * handoff + convergence proof can be exercised deterministically without a live vision call. */ readLobbyCodeFromFrame?: (frame: Buffer, apiKey: string) => Promise; /** * Local-tree packing DI seam (tests only, no npm dependency needed to exercise the route): * defaults to createLocalTreeArchive(root, opts) plus a host-side read of the produced archive * file into an ArrayBuffer (the SAME default cua-actor-lab.ts uses). Called ONCE per run, before * the ONE shared-plane sandbox is created, on the live local-tree route. */ packLocalTree?: (args: { root: string; extraExclude?: string[]; maxArchiveBytes?: number; }) => Promise<{ archive: LocalTreeArchive; buffer: ArrayBuffer; }>; } export interface RunSharedWorldLabOptions { 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?: SharedWorldLabHooks; /** Present only when the browser-route scorer hooks were CONFIG-DECLARED and loaded by the CLI * (#316); core-stamped onto the bundle as evidence. Absent for library callers. */ scorerProvenance?: RunScorerProvenance; } export type SharedWorldLabErrorCode = "HUMANISH_LAB_ANALYSIS_INVALID" | "HUMANISH_LAB_TASKS_UNSUPPORTED" | "HUMANISH_SHARED_WORLD_LAB_FAILED" | "HUMANISH_SHARED_WORLD_LAB_ACTOR_UNSUPPORTED" | "HUMANISH_SHARED_WORLD_LAB_INVALID" | "HUMANISH_SHARED_WORLD_LAB_KEYS_MISSING" | "HUMANISH_SHARED_WORLD_LAB_SUBJECT_ENV_MISSING"; /** One role seat's terminal outcome in the result projection. */ export interface SharedWorldRoleResult { id: string; index: number; persona: string; /** Terminal role status; "blocked" = fail-fast skipped it; "contract_proof_only" = dry-run. */ status: ActorStatus | "blocked" | "contract_proof_only"; ok: boolean; session?: { status: ActorStatus; completionReason: ActorCompletionReason; reason: string; screenshots: number; }; /** The user-data-dir profile this seat drove (proves per-seat isolation). */ profileDir: string; /** Set when the role was skipped by fail-fast (a pinned reason string). */ skippedReason?: string; error?: { code: SharedWorldLabErrorCode; message: string; }; } export interface SharedWorldLabResult extends AutomaticAnalysisResult { schema: typeof SHARED_WORLD_LAB_SCHEMA; /** True when the bundle verified AND (dry-run, or every role reached a terminal, engaged * verdict without a harness error). The roles' pass/fail is evidence, not the lab's exit code. */ ok: boolean; cwd: string; labId: string; /** The registry-resolved actor id that ran (or would run) the seats. */ actor: string; topology: "shared-world"; /** The DECLARED number of role seats. */ roleCount: number; /** The role ids that actually took a turn, in declared order. */ sequence: string[]; dryRun: boolean; runId: string; /** Live-only: the ONE shared sandbox's lifecycle proof (the stream/key value is never surfaced). */ sandbox?: { sandboxId: string; killed: boolean; }; /** Subject provenance (invariant 5): the ONE shared plane. */ subject?: RunSubjectProvenance; roles: SharedWorldRoleResult[]; observer?: ObserverResult; warnings: string[]; error?: { code: SharedWorldLabErrorCode; message: string; }; } /** A fully-resolved role seat (internal). */ interface RoleSpec { roleId: string; /** 0-based. */ roleIndex: number; simId: string; streamId: string; persona: ActorPersonaRef; instructions: string; /** Redacted original composed prompt for legacy study context; execution uses instructions. */ evidenceInstructions?: string; assignment?: RunStream["assignment"]; /** The role's declared device (a PROMPT SIGNAL — see the file's FIDELITY NOTE). */ deviceName: string; /** Lane override, then actor default; omitted preserves the provider default. */ reasoningEffort?: ReasoningEffort; /** Deterministic harness-owned completion guard. Lane-level override, else actor default. */ stopWhen?: StopWhen; /** A declared observation window (#510). Lane-level override, else actor default. */ dwell?: DwellWindow; entry?: string; seatUrl: string; screenshotDir: string; traceArtifactPath: string; profileDir: string; } /** One role seat's end-to-end run outcome (internal; projected into the result + the bundle). */ interface RoleOutcome { spec: RoleSpec; session?: CuaLoopResult; sessionError?: string; screenshots: string[]; desktopBrowser?: DesktopBrowserEvidence; desktopGeometry?: RunDesktopGeometry; /** Set when fail-fast skipped this role before it ran. */ skippedReason?: string; noEngagement: boolean; harnessError: boolean; /** The checkpoint snapshot taken AFTER this role's turn (absent for skipped roles). */ afterCheckpoint?: SharedWorldCheckpoint; } /** * Self-match-proof pkill/pgrep -f pattern for a seat's unique profile dir: bracket the first * character so the in-sandbox shell running the termination script (whose own command line * carries this pattern) can never match itself. Exported (pure) for contract tests. */ export declare function seatProfilePkillPattern(profileDir: string): string; /** * Build the in-sandbox command that ends ONE seat's browser when its turn ends (pure; exported * for contract tests). All roles share the ONE desktop, so a prior seat's browser left alive * could keep polling/holding websockets and mutating the shared plane during a later role's * turn, with its authenticated window one Alt-Tab away from the current actor. The recorded * launch PID (a setsid session leader) is the primary kill (its process group takes the whole * browser tree); pkill -f on the seat's unique profile dir is the fallback; a short bounded * wait escalates to SIGKILL. Exit is always 0: a termination failure degrades to the caller's * warning, never a failed run. */ export declare function buildSeatBrowserTerminationCommand(processId: string | undefined, profileDir: string): string; /** Combine a snapshot's per-probe digests into ONE sha256-16 (digest-only; no raw value). */ export declare function combineCheckpointDigest(parts: string[]): string; /** * Run ONE checkpoint snapshot LIVE: each declared probe runs read-only via the detached * primitive; its stdout is literal-scrubbed (provisioned values + the probe's declared redact * literals, folded into `scrub`) then pattern-redacted, then digested. Only the COMBINED digest * persists — never the raw value (the seed-step lockdown). Unique step names per snapshot prevent * stale-status reuse across snapshots. */ export declare function runCheckpointSnapshot(args: { desktop: E2BDesktopSandbox; snapshotIndex: number; name: string; checkpoints: LabSubjectStateCheckpoint[]; prevDigest: string | undefined; scrub: (text: string) => string; requestTimeoutMs: number; timers: DetachedTimers; }): Promise; /** The DECLARED (dry-run) checkpoint snapshot: digest the probe RECIPE (command digests), no run. */ export declare function declaredCheckpointSnapshot(name: string, checkpoints: LabSubjectStateCheckpoint[]): SharedWorldCheckpoint; /** sha256-16 over the ordered seed-step command digests — the seeded-state RECIPE identity. */ export declare function seedRecipeDigest(config: LabConfig): 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 runSharedWorldLab(options: RunSharedWorldLabOptions): Promise; /** Project the shared-world run into a humanish.run-bundle.v1 with the sharedWorld evidence block. */ export declare function buildSharedWorldBundle(args: { /** Lab provenance for the bundle\'s own `lab` field (#455). */ lab?: RunLabProvenance; config: LabConfig; descriptor: CuaActorDescriptor; createdAt: string; dryRun: boolean; runId: string; source: RunBundle["source"]; roleSpecs: RoleSpec[]; roleOutcomes: RoleOutcome[]; baselineCheckpoint: SharedWorldCheckpoint; subject: RunSubjectProvenance; sandboxResolution: [number, number]; sandboxPreset: DevicePreset; desktopGeometry?: RunDesktopGeometry; seedDigest: string; subjectCommit?: string; failFastReason?: string; desktopAllocated?: boolean; desktopLifetimeComplete?: boolean; }): RunBundle; export {};