import { BrowserSessionError, BrowserSessionManager } from './browser-sessions.js'; import type { BrowserAttachOptions, BrowserLaunchOptions } from './browser-sessions.js'; import { StaleElementError } from './browser-snapshot.js'; import type { BrowserProvisionReport, CardFieldGuard, OwnerApproval } from './browser-types.js'; import { UntrustedEffectError } from './browser-engine-contract.js'; import type { BrowserEngineOptions, BrowserExtractField, BrowserTarget } from './browser-engine-contract.js'; export { UntrustedEffectError }; export type { BrowserEngineOptions, BrowserExtractField, BrowserTarget }; /** * The browser capability itself: provisioning, sessions, and every page * operation, with no Agent-surface types in sight. */ export declare class BrowserEngine { private readonly sessions; private readonly options; private readonly snapshots; private readonly untrusted; private readonly cardGuard; private approval; constructor(sessions: BrowserSessionManager, options: BrowserEngineOptions); /** Whether this engine can be used to pay for something (see fill-card.ts). */ cardFieldGuardInstalled(): boolean; cardFieldGuard(): CardFieldGuard | null; /** * Strip live card material out of anything a page produced. Applied to every * value, name, body text, extracted field and ledger entry leaving this * class, unconditionally, see `CardFieldGuard` in browser-types.ts. */ private scrub; /** * Records that the owner asked for a specific outward action. * * Approval arrives mid-session, in the normal case: the agent reports what it * found on a page and the owner says to go ahead. Only the product's trust * contract can mint one of these, and only from a surface that carries * command authority, so nothing a page says can reach this. */ setOwnerApproval(approval: OwnerApproval | null): void; /** * Records that page content entered the conversation, with where it came * from AND what it said. * * The text matters as much as the origin. Without it, a later outward action * can only be judged on "did this turn read anything", which in a daemon is * permanently true, so every send would be refused and the boundary would * be switched off. With it, the guard can ask the answerable question: * does what is about to leave derive from what was read. */ private recordPageIngest; /** * Refuses an outward action when this turn has read page content. * * This is the composition that matters: a page the agent just read must not * be able to cause the agent to act outwards. The refusal names what to do * instead, which is to take it to the owner. * * `content` is what is about to be submitted, when the caller could work it * out. Supplying it turns the coarse question into the answerable one, does * this submission repeat what was read, which is the difference between a * form filled from the owner's instruction going through and every form on a * browsing session being refused. */ private requireOutwardEffectAllowed; /** * The values in the form this element belongs to, as the fields about to * leave the machine. * * Read live rather than remembered: the page may have filled, rewritten or * defaulted anything since the snapshot, and what matters is what will * actually be posted. Field names come from `name`/`id`, so a refusal can say * which input carried the overlap instead of pointing at an index. * * Password values are read but never returned. A password is high-entropy by * construction, so it contributes nothing to a derivation check, while * putting one into a `TaintFinding` excerpt would print it in a refusal * message, the check would have leaked what it was defending. * * Returns undefined when the values cannot be established: the element is * outside a form, the page is cross-origin-restricted, the evaluate times * out. That is the honest answer, and it drops this call to the coarse rule * rather than to a false "nothing overlaps". */ private static enclosingFormFields; sessionManager(): BrowserSessionManager; provision(options?: { readonly repair?: boolean | undefined; readonly allowDownload?: boolean | undefined; }): Promise; status(): Promise>; launch(options: BrowserLaunchOptions): Promise>; /** The one-act setup receipt for a call that may have provisioned something. */ private setupReceipt; attach(options: BrowserAttachOptions): Promise>; release(sessionId: string): Record; close(sessionId: string): Promise>; private resolveSessionId; private target; /** * Opens a browser only if nothing is open yet, so the first call a model * makes is a useful one instead of an error telling it to call launch first. */ private ensureSession; navigate(target: BrowserTarget, args: { readonly url: string; readonly waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | undefined; readonly timeoutMs?: number | undefined; readonly launch?: BrowserLaunchOptions | undefined; }): Promise>; snapshot(target: BrowserTarget, args?: { readonly limit?: number | undefined; }): Promise>; private currentSnapshot; /** * Refuses to drive a sign-in page. Called before every interactive action * (click, type, press, select), never before a read-only one, since * reading is exactly what lets the caller notice a sign-in page and hand * the URL back instead of clicking through it. See `credentialPageRefusal` * in browser-engine-contract.ts for what counts as a sign-in page and why: * this method is only the plumbing that gets it a URL and the last snapshot. */ private refuseCredentialInteraction; click(target: BrowserTarget, args: { readonly ref: string; readonly button?: 'left' | 'right' | 'middle' | undefined; readonly clickCount?: number | undefined; readonly timeoutMs?: number | undefined; }): Promise>; /** * Types into a resolved element. There is no variant that types into "the * focused window": the text goes to this element in this page or the call * fails. */ type(target: BrowserTarget, args: { readonly ref: string; readonly text: string; readonly submit?: boolean | undefined; readonly replace?: boolean | undefined; readonly timeoutMs?: number | undefined; }): Promise>; /** * Type several DAEMON-held values in one motion, every ref resolved against * the snapshot in place before any of them is typed. Values never come from * the model. The mechanics live in browser-secret-fill.ts. * * The caller arms the guard once before calling this, for the whole batch. * * A field-by-field version of this cleared the snapshot after every field, * so the second field's ref pointed at nothing by the time its turn came. * This clears it once, only on failure: nothing then types into a page * nobody will click. A full success leaves the snapshot in place, still * fresh since nothing here navigates, so the submit click a filled card is * always followed by still resolves; that click clears it, as every click * already does. */ fillSecretBatch(target: BrowserTarget, args: { readonly fills: readonly { readonly ref: string; readonly value: string; }[]; readonly timeoutMs?: number | undefined; }): Promise>; select(target: BrowserTarget, args: { readonly ref: string; readonly values: readonly string[]; readonly timeoutMs?: number | undefined; }): Promise>; press(target: BrowserTarget, args: { readonly ref: string; readonly key: string; readonly timeoutMs?: number | undefined; }): Promise>; scroll(target: BrowserTarget, args: { readonly ref?: string | undefined; readonly direction?: 'up' | 'down' | undefined; readonly amount?: number | undefined; }): Promise>; waitFor(target: BrowserTarget, args: { readonly text?: string | undefined; readonly url?: string | undefined; readonly timeoutMs?: number | undefined; }): Promise>; readText(target: BrowserTarget, args?: { readonly maxChars?: number | undefined; }): Promise>; screenshot(target: BrowserTarget, args?: { readonly fullPage?: boolean | undefined; readonly path?: string | undefined; }): Promise>; tabs(target: BrowserTarget): Promise>; newTab(target: BrowserTarget, args?: { readonly url?: string | undefined; readonly launch?: BrowserLaunchOptions | undefined; }): Promise>; switchTab(target: BrowserTarget, args: { readonly pageId: string; }): Record; closeTab(target: BrowserTarget, args: { readonly pageId: string; }): Promise>; goBack(target: BrowserTarget): Promise>; goForward(target: BrowserTarget): Promise>; /** * Reads data out of the page. * * This replaced an `evaluate` action that ran caller-supplied JavaScript in * the page. That action was guarded by searching the source for `fetch`, * `sendBeacon` and friends, a denylist standing between attacker-influenced * text and arbitrary code execution, which is a losing shape. An expression * built as `globalThis[atob('ZmV0Y2g=')]` defeats a string match while doing * exactly what the match existed to stop. * * So there is no longer any way to express code here. The caller supplies a * CSS selector or a ref and names the fields it wants; the function that runs * in the page is fixed, ships in this file, and reads DOM properties. A * network call is not something this contract can describe, not something we * try to notice. * * What that costs: running page functions, computing values in-page, and * poking at application state that never reaches the DOM. Interaction still * happens through click, type, select and press, which are checked; anything * computed can be computed here, from extracted data, where a page cannot * reach it. */ extract(target: BrowserTarget, args: { readonly ref?: string | undefined; readonly selector?: string | undefined; readonly fields?: readonly BrowserExtractField[] | undefined; readonly all?: boolean | undefined; readonly limit?: number | undefined; }): Promise>; shutdown(): Promise; } export { BrowserSessionError, StaleElementError }; //# sourceMappingURL=browser-engine.d.ts.map