UNPKG

agents

Version:

A home for your AI agents

668 lines (666 loc) • 24.7 kB
import { d as LifecycleRouteContext, s as LifecycleCapability } from "./capability-runner-BiVUtjqC.js"; import { n as InternalHostInfo, r as InternalHostMessage, s as InternalHostsOptions } from "./internal-hosts-Kfp5A392.js"; import { O as CdpConnection, a as ListableBrowserSessionStore, d as BrowserBinding, m as BrowserSessionGuardrails, s as BrowserLiveView, u as LiveViewMode } from "./session-store-B7Ca-u1t.js"; //#region src/browser/spec.d.ts /** * The element type of an array field or type. `$ref` is always * domain-qualified (`"Network.Cookie"`), matching a type's `name`. */ interface CdpItems { type?: string; $ref?: string; enum?: string[]; } /** * A command parameter, return value, event parameter, or type property. * Either `type` (a JSON type such as `"string"` or `"array"`) or `$ref` * (a domain-qualified type name matching a type's `name`) is set. */ interface CdpField { name: string; description?: string; optional?: boolean; experimental?: boolean; deprecated?: boolean; type?: string; $ref?: string; enum?: string[]; items?: CdpItems; } interface SearchableCdpSpec { domains: Array<{ name: string; description?: string; experimental?: boolean; deprecated?: boolean; commands: Array<{ name: string; method: string; description?: string; experimental?: boolean; deprecated?: boolean; parameters: CdpField[]; returns: CdpField[]; }>; events: Array<{ name: string; event: string; description?: string; experimental?: boolean; deprecated?: boolean; parameters: CdpField[]; }>; types: Array<{ id: string; name: string; description?: string; experimental?: boolean; deprecated?: boolean /** JSON type of the value, e.g. `"string"`, `"object"`, `"array"`. */; type?: string; enum?: string[]; properties: CdpField[]; items?: CdpItems; }>; }>; } interface CdpSpecSource { /** Browser Rendering binding (Fetcher) — used in production */ browser?: BrowserBinding; /** CDP base URL override (e.g. http://localhost:9222) */ cdpUrl?: string; /** Headers to send with CDP URL discovery requests */ cdpHeaders?: Record<string, string>; /** * An existing Browser Run session to read the protocol from. Without it, * loading through the binding creates (and deletes) a throwaway session. */ sessionId?: string; } /** Load the (cached) searchable CDP spec for a browser source. */ declare function loadCdpSpec(source: CdpSpecSource): Promise<SearchableCdpSpec>; //#endregion //#region src/browser/session.d.ts /** * What a {@link BrowserSession} connects to. Usually a `Browser` installed on * the host's Lifecycle. */ interface BrowserSource { /** The browser's name, for log messages. */ readonly name: string; /** * Reattach to the browser (or replace it) and open a CDP connection for * a scope. A source that ignores `scope` shares one active tab between * every caller. */ connect(options?: BrowserConnectOptions): Promise<BrowserConnection>; /** * The browser's internal hostnames, when it has any. The docs list them, * and {@link BrowserSession.functions} adds the hosts functions. */ readonly internalHosts?: BrowserInternalHosts; } interface BrowserSessionOptions { /** * Who is driving: sessions with the same scope share an active tab, and * each scope keeps its own. Cookies and logins are shared by every scope. * Defaults to the browser's default scope. */ scope?: string; /** * The largest `Page.captureScreenshot` result `send` returns, measured as * JSON. Defaults to 1,000,000 bytes. */ maxResultBytes?: number; /** * Lock the scope on this browser: the session takes the scope's lock at * its first call (or at {@link BrowserSession.lock}) and holds it until it * closes. Other locking sessions in the scope wait for the lock, in order, * rather than fail, so their calls never interleave. Sessions without it * don't wait and aren't waited for. Locks are per isolate, which is per * Durable Object. * * Use it when several runs in one scope can start together (parallel * tool calls, say) and must not see each other's navigation. * * Only `close()` releases the lock: not aborting a signal, losing the * browser, or dropping the session. Close a locking session in `finally`, * even when connecting failed, or its scope stays locked until the * isolate restarts. */ lock?: boolean; } /** {@link BrowserSession.lock}'s options. */ interface BrowserLockOptions { /** * Bounds this call's wait: when it aborts, this call rejects with its * reason. The session keeps waiting while other calls still are; once * none is, it leaves the queue at once, so it doesn't hold up the * sessions behind it. A call without a signal, including the wait a * session's first function call makes, never gives up. The signal does * nothing once the lock is held: only `close()` releases it. */ signal?: AbortSignal; } /** `send`'s input. */ interface BrowserSendInput { /** CDP method, e.g. `"Page.navigate"`. */ method: string; /** CDP command parameters. */ params?: Record<string, unknown>; /** * `"active"` for the current tab, or a handle from `attachToTarget`. Omit * for `Browser.*` and `Target.*` commands. */ sessionId?: string; /** Per-command timeout in milliseconds. */ timeoutMs?: number; } /** `attachToTarget`'s input. */ interface BrowserAttachInput { /** Target id from `Target.getTargets` or `Target.createTarget`. */ targetId: string; timeoutMs?: number; } /** `getDebugLog`'s input. */ interface BrowserDebugLogInput { /** Most entries to return, newest last. Defaults to 50; 400 are kept. */ limit?: number; } /** A tab a page opened on its own during a session. */ interface BrowserNewTab { targetId: string; url?: string; title?: string; } /** * What happened to the browser during one session, beyond the results of its * calls. {@link BrowserSession.close} returns it. */ interface BrowserSessionReport { /** The browser was replaced before this session; earlier page state is gone. */ restarted: boolean; /** Tabs the page opened itself (popups, `target=_blank`). */ newTabs: BrowserNewTab[]; /** * The tab this scope was working in was closed by someone else (another * scope, or a person in Live View), so `"active"` will be a new blank tab. */ tabClosed: boolean; /** Another scope is working in this scope's active tab too. */ tabShared: boolean; /** * The report as sentences for the model, one per thing that happened, * empty when nothing did. Show them after the run's result. */ notices: string[]; } /** `sessionId` value that addresses the tab the agent is working in. */ declare const ACTIVE_PAGE_SESSION = "active"; /** * One run's connection to a persistent browser, for one scope. * * It connects on its first call and stays connected until {@link close}. * Concurrent first calls share one connect. Every method takes one options * object, validated before anything touches the browser, because the input * usually comes from model-written code. * * Create one per run with `browser.session({ scope })`, and close it when * the run ends. The browser itself stays open. Pass `lock: true` when * runs in one scope may overlap; see {@link BrowserSessionOptions.lock}. * * @experimental The API surface may change before stabilizing. */ declare class BrowserSession { #private; constructor(browser: BrowserSource, options?: BrowserSessionOptions); /** * The `browser.*` functions bound to this session, by name: the ones * `Browser.functionDocs()` describes. Each takes one input object and * validates it, so model-written code can call them directly. Like the * session's methods, every one rejects once the session is closed, and * waits for the scope's lock when the session locks. */ get functions(): Readonly<Record<string, BrowserFunction>>; /** * Take this scope's lock now rather than at the first call, and hold it * until {@link close}. For sessions opened with `lock: true`; a * caller with its own time limit calls it first, so the wait doesn't count * against the limit, or passes `signal` to bound the wait. * * Resolves once the session holds the lock, after the locking sessions * ahead of it close; later and concurrent calls resolve with the same * lock. Rejects when the session closes while waiting. Each call's * `signal` bounds only that call's wait: it rejects with the signal's * reason, and the session leaves the queue once no call is still * waiting (see {@link BrowserLockOptions.signal}); a later call or first * call waits again. */ lock(options?: BrowserLockOptions): Promise<void>; /** * How long this session waited for its lock, in milliseconds: `0` until * it has waited, and for sessions that don't lock. */ get waitedForLockMs(): number; /** * Send a CDP command and resolve with its result, without the JSON-RPC * envelope. `sessionId: "active"` runs it in the scope's tab. */ send(input: BrowserSendInput): Promise<unknown>; /** * Make an open tab the scope's active tab, and resolve with a `sessionId` * handle for it that keeps working in later runs. */ attachToTarget(input: BrowserAttachInput): Promise<{ sessionId: string; }>; /** The Chrome DevTools Protocol description the browser serves. */ spec(): Promise<SearchableCdpSpec>; /** Recent CDP traffic on this session's connection, newest last. */ getDebugLog(input?: BrowserDebugLogInput): Promise<unknown[]>; /** Forget the CDP traffic recorded so far. */ clearDebugLog(): Promise<null>; /** * End the session: save the scope's active tab, work out what happened to * the browser, and close the connection. The browser stays open. * * Resolves with the report, or `undefined` when the session never * connected. Never rejects: failures to read the browser's state are * logged and leave the report incomplete. Later calls resolve with * `undefined`, and other methods reject. * * A locking session releases the lock once the active tab is saved, or * stops waiting for it if it was still waiting. */ close(): Promise<BrowserSessionReport | undefined>; } //#endregion //#region src/browser/browser-functions.d.ts /** * One function on the `browser` object, as the host runs it: it takes one * input object (usually from model-written code, so it's validated) and * resolves with a JSON-safe result. * * @experimental The API surface may change before stabilizing. */ type BrowserFunction = (input: unknown) => Promise<unknown>; /** * What the model needs to know to use the `browser` object, for a tool or * module description. `Browser.functionDocs()` returns it. * * @experimental The API surface may change before stabilizing. */ interface BrowserFunctionDocs { /** * The `browser.*` functions in Markdown, one bullet per function, then the * browser's internal sites when it has any. */ functions: string; /** * How to use the browser, one rule per entry, without bullets. Rules about * how the code runs (timeouts, how screenshots reach the model) belong to * whatever runs it, so add those yourself. */ rules: string[]; } //#endregion //#region src/browser/providers/browser-run.d.ts /** * Browser Run settings applied every time a browser is created — including * when a lost browser is replaced — so options like guardrails survive a * restart. */ interface BrowserRunOptions<S = Record<string, unknown>> { /** * Platform `keep_alive` in milliseconds: how long an idle browser lives. * Defaults to {@link BROWSER_SESSION_KEEP_ALIVE_DEFAULT_MS} (10 minutes). * Browser Run accepts up to {@link BROWSER_SESSION_KEEP_ALIVE_MAX_MS} * (20 minutes). */ keepAliveMs?: number; /** Opt into Browser Run session recording (rrweb capture). */ recording?: boolean; /** * Hostname guardrails, fixed at launch for every connection. They don't * apply to `internalHosts`: `allowedDomains: []` leaves the browser able * to open internal hostnames only. */ guardrails?: BrowserSessionGuardrails; /** * Hostnames only this browser can open, served by your agent or a Worker * instead of the internet. Plain http on port 80; no HTTPS or WebSockets. * Deployed Workers only: in local dev the browser starts without them. * * Any http page open in the browser can send requests to these hosts, so * a host whose requests have side effects should check `Origin` (see * `InternalHost.fetch`). * * @example * ```ts * browserRun(this.env.BROWSER, { * internalHosts: { * router: this.ctx.exports.InternalHostRouter, * hosts: { * "preview.internal": { * description: "A dev server in your sandbox", * target: z.object({ port: z.number().int() }), * fetch: (request, { port }) => this.sandboxPort(port, request) * } * } * } * }); * ``` */ internalHosts?: InternalHostsOptions<S>; } /** A {@link Browser} provider backed by Cloudflare Browser Run. */ interface BrowserRunProvider { readonly kind: "browser-run"; readonly binding: BrowserBinding; readonly options: BrowserRunOptions; } /** * Run the browser on Cloudflare Browser Run. * * @example * ```ts * new Browser({ provider: browserRun(env.BROWSER, { recording: true }) }); * ``` */ declare function browserRun<const S extends Record<string, unknown> = {}>( binding: BrowserBinding, options?: BrowserRunOptions<S> ): BrowserRunProvider; //#endregion //#region src/browser/scope.d.ts /** * Browser scopes: who is driving a shared `Browser`. Sessions with the same * scope share an active tab (and, when they lock, a lock); each scope * keeps its own. Cookies and logins are shared by every scope. */ /** * The scope used when a caller doesn't pick one: every caller that leaves * `scope` out shares this one active tab. See `BrowserConnectOptions`. */ declare const DEFAULT_BROWSER_SCOPE = "shared"; /** * The scope for one conversation: `conversation:<id>`. pi's `browserTool` * gives each conversation this scope by default, so app code that opens * sessions with the same scope (a Workspace's `codemode:browser`, say) lands * in the same tab as the tool. pi's conversation ids are numbers: pass * `String(conversationId)`. * * Throws on a missing or empty id rather than returning * `"conversation:undefined"`, which would quietly put every conversation * without an id in one tab. * * @experimental The API surface may change before stabilizing. */ declare function conversationScope(id: string): string; //#endregion //#region src/browser/browser.d.ts /** * Scope records kept per browser. Writing a new one past the cap deletes the * least recently used; a scope that comes back afterwards starts fresh. */ declare const MAX_BROWSER_SCOPES = 100; /** Where a {@link Browser} gets its browser from. */ type BrowserProvider = BrowserRunProvider; interface BrowserOptions { /** Where the browser runs. Create one with `browserRun()`. */ provider: BrowserProvider; /** * Which browser this is. Two `Browser` objects on the same Durable Object * need different names. Defaults to {@link DEFAULT_BROWSER_NAME}. */ name?: string; /** * Where the browser's record lives. Defaults to a * {@link DurableBrowserSessionStore} over the host object's storage, which * requires installing the `Browser` with `Lifecycle.use()`. Passing a store * lets the `Browser` work without Lifecycle. It must implement `list`. */ store?: ListableBrowserSessionStore; /** Default CDP command timeout for {@link Browser.connect}. */ timeoutMs?: number; /** * Minimum interval between activity-driven `updatedAt` refreshes. * Defaults to {@link SESSION_TOUCH_INTERVAL_MS}, capped at half the * keep-alive window. * * @internal For tests. */ touchIntervalMs?: number; } interface ResolvedBrowser { name: string; /** The provider's session id — host-side only, never model-visible. */ sessionId: string; /** * `true` when this resolution had to create a fresh browser to replace one * that previously existed (was closed, or expired or died upstream). Page * state from the prior browser is gone; surface this loudly to the model. * `false` only on first-ever use of the name — nothing was lost. */ restarted: boolean; createdAt: number; updatedAt: number; } interface BrowserConnectOptions { /** * Who is driving. Connections with the same scope share an active tab, and * each scope keeps its own, so two conversations on one browser don't * drive each other's page. Cookies, logins, and the tabs themselves are * shared by every scope: a scope splits "which tab is mine", not the * browser. Defaults to {@link DEFAULT_BROWSER_SCOPE}. */ scope?: string; } interface BrowserConnection { name: string; sessionId: string; /** The scope this connection works in; see {@link BrowserConnectOptions}. */ scope: string; /** * `true` when this scope worked in an earlier browser that has since been * replaced, whichever scope's connection replaced it. Its tabs and page * state are gone; surface this loudly to the model. `false` on a scope's * first connection, even one that replaced a lost browser: the scope had * nothing to lose. This is per scope, not a browser-wide signal. */ restarted: boolean; /** * The tab this scope last worked in, when it recorded one in this * browser. */ activeTargetId?: string; /** Closing this connection does NOT close the browser. */ cdp: CdpConnection; /** * Record the tab this scope is working in (or clear it with `undefined`). * Never resurrects: returns `false` when the browser was closed or * replaced since this connection resolved it. */ setActiveTarget(targetId: string | undefined): Promise<boolean>; /** The tabs other scopes are working in, in this browser. */ targetsInOtherScopes(): Promise<Set<string>>; /** * The Chrome DevTools Protocol description this browser serves, read from * the browser itself (cached). */ spec(): Promise<SearchableCdpSpec>; } /** * A browser's internal hostnames (`browserRun({ internalHosts })`), for the * host and the browser tool. Assignments are stored with the agent, so they * survive the browser being replaced, eviction, and redeploys. */ interface BrowserInternalHosts { /** Every hostname with its description, schema, and current target. */ list(): Promise<InternalHostInfo[]>; /** * Point an assignable host somewhere: `target` is checked against the * host's `target` schema and stored. The browser's next request to the * host uses it. Throws with what's wrong when the target doesn't match. */ assign( host: string, target: unknown ): Promise<{ url: string; }>; /** Forget a host's target; its requests answer 502 until assigned again. */ clear(host: string): Promise<boolean>; /** The hostnames with their descriptions and schemas, without reading storage. */ describe(): InternalHostInfo[]; } /** * One named, persistent browser. * * Install it with `Lifecycle.use()` — on an Agent subclass or any Durable * Object that composes `Lifecycle` — and it stores its record in the * object's own storage. It schedules nothing: the provider reclaims idle * browsers itself (Browser Run once `keep_alive` elapses), and the next use * replaces the lost browser with `restarted: true`. * * Everything here is host-side. Models never see browser names, provider * session ids, or Live View URLs through this surface. * * Store discipline: locks wrap storage operations only — liveness probes and * provider create/close calls always happen outside any lock, with a commit * re-check to detect concurrent swaps. * * @experimental The API surface may change before stabilizing. */ declare class Browser extends LifecycleCapability { #private; /** Which browser this is; see {@link BrowserOptions.name}. */ readonly name: string; constructor(options: BrowserOptions); /** * Reattach to the browser when it is still alive, otherwise create a fresh * one. See {@link ResolvedBrowser.restarted} for the mortality signal. */ resolve(): Promise<ResolvedBrowser>; /** * Resolve the browser and open a CDP connection to it for one scope (see * {@link BrowserConnectOptions.scope}). Commands sent over the connection * refresh the record's `updatedAt` (throttled to * {@link SESSION_TOUCH_INTERVAL_MS}), so hosts can tell an actively used * browser from one the platform has likely reclaimed. * * If the browser expires between the liveness probe and the WebSocket * upgrade, the record is retired and the browser resolved once more, so * the caller gets a fresh browser instead of an error. */ connect(options?: BrowserConnectOptions): Promise<BrowserConnection>; /** * Start a session on this browser for one run: connects on its first * call, resolves `sessionId: "active"` to the scope's tab, and reports * what happened to the browser when you close it. `browserTool` opens * one per run. */ session(options?: BrowserSessionOptions): BrowserSession; /** * What the model needs to know to use this browser's `browser.*` * functions ({@link BrowserSession.functions}): the functions in Markdown, * including the internal sites and hosts functions this browser has, and * the rules for using them. For a tool or module description; add rules * about how your code runs (timeouts, screenshots) yourself. */ functionDocs(): BrowserFunctionDocs; /** * Close the browser: retire the record (so the next resolve reports * `restarted: true`) and close its provider session. Returns `false` when * there was nothing to close. * * The provider close is best-effort: the retired record is the durable * outcome, and a browser whose close failed is unreachable through this * store, so the provider reclaims it once idle (Browser Run: after * `keepAliveMs`, at most 20 minutes). */ close(): Promise<boolean>; /** * Mint Live View URLs for the browser's open tabs, fresh from a target * listing. `undefined` when the browser was never created, is closed, or * is gone. The URLs are bearer credentials with a short connect window * (~5 min) — hand them to a trusted human, never to the model, and never * store them: re-mint instead. */ liveView(options?: { mode?: LiveViewMode; }): Promise<BrowserLiveView | undefined>; /** * The browser's internal hostnames, or `undefined` when its provider has * none. See {@link BrowserInternalHosts}. */ get internalHosts(): BrowserInternalHosts | undefined; /** * Serve one browser request to an internal hostname. `InternalHostRouter` * calls the agent's `fetchInternalHost()` with the message; `Agent` passes * it here on its own. A plain Durable Object passes it on itself: * * ```ts * fetchInternalHost(message: InternalHostMessage) { * return this.browser.fetchInternalHost(message); * } * ``` * * Answers 502 with the reason when this browser doesn't serve the host. */ fetchInternalHost(message: InternalHostMessage): Promise<Response>; /** * `Agent.fetchInternalHost()` routes here through Lifecycle, so `Agent` * needn't import the browser. * * @internal Called by Lifecycle; not part of the host API. */ onRoute(context: LifecycleRouteContext): Promise<unknown>; } //#endregion export { loadCdpSpec as A, BrowserSessionOptions as C, CdpItems as D, CdpField as E, CdpSpecSource as O, BrowserSession as S, BrowserSource as T, BrowserAttachInput as _, BrowserOptions as a, BrowserNewTab as b, ResolvedBrowser as c, BrowserRunOptions as d, BrowserRunProvider as f, ACTIVE_PAGE_SESSION as g, BrowserFunctionDocs as h, BrowserInternalHosts as i, SearchableCdpSpec as k, DEFAULT_BROWSER_SCOPE as l, BrowserFunction as m, BrowserConnectOptions as n, BrowserProvider as o, browserRun as p, BrowserConnection as r, MAX_BROWSER_SCOPES as s, Browser as t, conversationScope as u, BrowserDebugLogInput as v, BrowserSessionReport as w, BrowserSendInput as x, BrowserLockOptions as y }; //# sourceMappingURL=browser-CI97khiJ.d.ts.map