*** title: Runtimes description: >- Runtimes are remote resources running on BCTRL infrastructure that you control via SDK or HTTP. ---------------- A runtime is a remote resource — a browser, desktop, or spreadsheet — running on BCTRL infrastructure inside a workspace. You don't run anything locally. Your code sends commands over the network and gets results back. Today, BCTRL supports **browser runtimes**. Desktop and spreadsheet runtimes are coming. ## Launching a browser runtime Every runtime needs an **alias** (a name you choose) and a **driver** (how you want to interact with it): ```ts const runtime = await workspace.runtimes .browser("main") .playwright({ mode: "ephemeral" }); ``` The alias `"main"` identifies this runtime within the workspace. You can run multiple runtimes concurrently — each with its own alias. ## Drivers Pick the driver that matches your automation style: Full Playwright API — `page.goto()`, `page.locator()`, `page.evaluate()`. Best for structured automation with strong selector support. Puppeteer API — familiar if you're coming from Chrome DevTools Protocol workflows. AI-native driver — `stagehand.act()`, `stagehand.extract()`, `stagehand.observe()`. Best for agents that reason about page content rather than using selectors. WebDriver protocol — `driver.get()`, `driver.findElement()`. Familiar to enterprise automation teams. ```ts // Playwright const pw = await workspace.runtimes.browser("a").playwright(); // Puppeteer const pp = await workspace.runtimes.browser("b").puppeteer(); // Stagehand const sh = await workspace.runtimes.browser("c").stagehand(); // Selenium const se = await workspace.runtimes.browser("d").selenium(); ``` All four drivers connect to the same remote browser — the driver just determines the API surface you use to control it. ## Launch modes ### Ephemeral A fresh browser with no saved state. Destroyed when the runtime stops. ```ts const runtime = await workspace.runtimes.browser("quick").playwright({ mode: "ephemeral", }); ``` ### Ephemeral with config Customize the browser's behavior: ```ts const runtime = await workspace.runtimes.browser("stealth").playwright({ mode: "ephemeral", config: { humanize: true, solveCaptchas: true, }, extensionIds: ["captcha-solver-ext"], }); ``` ### Profile-backed Use a persistent browser profile with saved cookies, storage, and fingerprint: ```ts const runtime = await workspace.runtimes.browser("crm").playwright({ mode: "profile", profileId: "sales-bot", }); ``` Profiles are managed via `bctrl.browserProfiles` and persist across runtime restarts. ## Working with a runtime Once launched, the runtime gives you the driver's native API surface: ```ts // Navigate await runtime.page.goto("https://example.com"); // Extract const title = await runtime.page.title(); const html = await runtime.page.content(); // Interact await runtime.page.locator("#search").fill("bctrl"); await runtime.page.locator("#submit").click(); // Screenshot const screenshot = await runtime.page.screenshot(); ``` See [Browser Capabilities](/sdk/browser-capabilities) for the full reference. ## AI agent execution Runtimes with Stagehand or browser-use support agent-level commands: ```ts // Stagehand — action by instruction await runtime.stagehand.act("Click the login button"); // Stagehand — structured extraction const data = await runtime.stagehand.extract( "Extract the product name and price", z.object({ name: z.string(), price: z.string() }) ); // Browser-use — autonomous agent const op = await runtime.browserUse.agent.execute( "Export the latest invoices as CSV" ); ``` Long-running agent calls return an **operation** that you can poll or wait on: ```ts const result = await workspace.operations.wait(op.id, { until: "completed", }); ``` ## Viewing and embedding Get a live interactive view or a recording of a runtime: ```ts // Live view — interactive or view-only const live = await runtime.live({ interactive: true }); console.log(live.url); // Recording — replay after the fact const recording = await runtime.recording(); console.log(recording.url); ``` ## Multiple concurrent runtimes A workspace can run multiple runtimes simultaneously: ```ts const [crm, email, research] = await Promise.all([ workspace.runtimes.browser("crm").playwright({ mode: "profile", profileId: "crm-bot" }), workspace.runtimes.browser("email").playwright({ mode: "profile", profileId: "email-bot" }), workspace.runtimes.browser("research").playwright({ mode: "ephemeral" }), ]); // Each runtime operates independently await crm.page.goto("https://crm.example.com"); await email.page.goto("https://mail.example.com"); await research.page.goto("https://google.com"); ``` *** ## Base runtime methods These methods are available on every runtime regardless of driver. They work the same across Playwright, Puppeteer, Stagehand, and Selenium. ### run(request) Run structured automation steps directly against the runtime. This is the low-level interface that all driver methods use under the hood. ```ts const result = await runtime.run({ steps: [ { call: "page.goto", args: ["https://example.com"] }, { call: "page.screenshot" }, ], }); ``` | Parameter | Type | Required | Description | | ---------------- | --------------- | -------- | ----------------------------- | | `steps` | `ExecuteStep[]` | Yes | Array of steps to execute | | `idempotencyKey` | `string` | No | Deduplication key for retries | | `mode` | `'fail-fast'` | No | Stop on first error | Each step has a `call` (method name), optional `args` (array), optional `ref` (target ID), and optional `bind` (alias for the result). Steps execute in order. **Returns** `WorkspaceExecuteResponse` ### stop() Stop the runtime and release infrastructure. Profile-backed runtimes save their state before stopping. ```ts await runtime.stop(); ``` **Returns** `Runtime` — the final runtime state. ### live(options?) Get an embeddable live view of the browser. Can be interactive (control the browser) or view-only. ```ts const live = await runtime.live({ interactive: true }); console.log(live.iframeUrl); ``` | Parameter | Type | Required | Description | | ------------- | --------- | -------- | ------------------------------------------------- | | `interactive` | `boolean` | No | Allow mouse/keyboard interaction (default: false) | **Returns** `{ iframeUrl: string }` ### recording() Get a recording replay of the runtime's session. ```ts const recording = await runtime.recording(); console.log(recording.iframeUrl); ``` **Returns** `{ iframeUrl: string }` ### state() Query the current state of the runtime — which pages are open, the current URL, driver info. ```ts const state = await runtime.state(); ``` **Returns** `WorkspaceStateResponse` ### events.list(query?) List events that have occurred in the runtime — navigation, console logs, errors, page creation. ```ts const events = await runtime.events.list({ eventTypes: ["page.navigated", "console"], limit: 50, }); ``` | Parameter | Type | Required | Description | | ------------ | ---------- | -------- | ------------------------------------ | | `after` | `string` | No | Cursor — return events after this ID | | `limit` | `number` | No | Max events to return | | `eventTypes` | `string[]` | No | Filter by event type | | `pageId` | `string` | No | Filter by page ID | ### events.wait(request?, options?) Wait for an event matching the given criteria. Blocks until a match or timeout. ```ts const event = await runtime.events.wait({ eventTypes: ["page.navigated"], timeoutMs: 10_000, }); ``` | Parameter | Type | Required | Description | | ------------ | ---------- | -------- | --------------------------------- | | `after` | `string` | No | Wait for events after this cursor | | `timeoutMs` | `number` | No | Max wait time in milliseconds | | `eventTypes` | `string[]` | No | Event types to match | | `pageId` | `string` | No | Filter by page ID | ### captcha.detect(options?) Detect captchas present on the current page. ```ts const result = await runtime.captcha.detect(); if (result.detected) { console.log(result.type); // e.g., "recaptcha", "hcaptcha" } ``` ### captcha.solve(options?) Attempt to solve a detected captcha. ```ts const result = await runtime.captcha.solve(); ``` ### stagehand.act(instruction, options?) Execute a natural-language instruction. ```ts await runtime.stagehand.act("Click the login button"); ``` ### stagehand.extract(instruction, schema, options?) Extract structured data from the page using natural language. ```ts import { z } from "@bctrl/sdk"; const data = await runtime.stagehand.extract( "Extract the product name and price", z.object({ name: z.string(), price: z.string() }) ); ``` ### stagehand.observe(instruction?, options?) Observe the page and identify interactive elements. ```ts const elements = await runtime.stagehand.observe("Find all clickable buttons"); ``` ### stagehand.agent(config?) Create a Stagehand agent for multi-step autonomous tasks. ```ts const agent = runtime.stagehand.agent(); const result = await agent.execute("Navigate to settings and export data as CSV"); ``` ### stagehand.getMetrics() / stagehand.getHistory() ```ts const metrics = await runtime.stagehand.getMetrics(); const history = await runtime.stagehand.getHistory(); ``` ### browserUse.agent(config?).execute(task, options?) Run an autonomous browser-use agent. ```ts const op = await runtime.browserUse.agent().execute( "Export the latest invoices as CSV" ); const result = await workspace.operations.wait(op.id, { until: "completed", }); ``` ### browserUse.codeAgent(config?).execute(task, options?) Run a browser-use code agent (generates and executes code). ```ts const op = await runtime.browserUse.codeAgent().execute( "Scrape all product listings into a structured table" ); ``` ## Related * [Workspaces](/sdk/concepts/workspaces) — the container that holds runtimes * [Scopes & Inheritance](/sdk/concepts/scopes-and-inheritance) — how workspace mounts flow to runtimes * [Browser Capabilities](/sdk/browser-capabilities) — full driver method reference * [API Reference: Runtimes](/api/workspaces) — the underlying HTTP endpoints