/** * The ports of the cairn engine (invariant #2: add behavior by implementing one of these, * never by branching inside a stage). Core depends only on these; `../adapters` implement them. */ import type { Assertion, AssertionResult, Context, Evidence, LlmUsage, PageElement, Result, Scenario, SettleOptions, Step, Target, Verdict } from "./types.js"; import type { TraceEvent } from "./trace.js"; /** Grounding from any source (NL, git diff, ticket, RAG). */ export interface ContextProvider { provide(task: string): Promise; } /** * Intent → ordered Scenario for the plan-then-execute pipeline (frozen replay uses no LLM, invariant #4). * Discovery is NOT a Planner: `discover()` is a separate free function that interleaves observe→act→adapt * against the live browser (invariant #3), so it can't be a pure `plan(ctx)`. The CLI's `discover` calls * it directly, outside the Harness/Planner pipeline. */ export interface Planner { plan(ctx: Context): Promise; } /** Drives a browser. Replaceable without touching core (invariant #5); resolves targets from intent, not handles. * * Lifecycle: whoever constructs a Driver owns it — the engine closes only drivers it created * (`runScenario`'s default); a caller-supplied driver is closed by the caller. `close()` ends the * session permanently: a closed driver must not be reused — construct a new instance instead (#98). * * Trusted input: interaction methods (`click`/`type`/`select`/…) must dispatch *trusted*, * user-level events (CDP input, real key/mouse), never a synthetic JS `.click()` — a controlled * component ignores untrusted events, so a shortcut would silently no-op. The reference driver * (Chrome DevTools MCP) satisfies this; a custom driver must too. * * Perception is a11y-native: `snapshot()` reports what the accessibility tree exposes — cairn * perceives like assistive tech, so a control whose state lives outside a11y (a custom widget with * no `aria-checked`/role/name — an accessibility violation) is *invisible or mis-reported to cairn * exactly as it is to a screen reader*. The engine does not special-case app-specific DOM to work * around this (invariant #1). A consumer whose app has such widgets injects corrected perception by * wrapping `snapshot()` in its own Driver — the sanctioned seam — while the real fix is the app * exposing proper ARIA state. */ export interface Driver { goto(url: string): Promise; click(target: Target): Promise; doubleClick(target: Target): Promise; hover(target: Target): Promise; type(target: Target, text: string): Promise; /** Resolve a target and return it enriched with resilient locators (role, structural index) for freezing. */ locate(target: Target): Promise; /** Choose an option in a dropdown by its value — native `