Invocations

View as Markdown

An invocation is hosted agent work BCTRL runs inside a runtime. Rather than driving the browser yourself over CDP, you describe what you want in natural language and BCTRL executes it: a single Stagehand action, a structured extraction, or a multi-step browser-use agent.

Invocations are durable, cancellable, and awaitable. You create them against a runtime (live control) and read their history from the run (observability).

Create and wait

createAndWait creates the invocation and polls until it finishes - the common case when you just want the result.

1import { z } from "zod";
2
3const invocation = await bctrl.runtimes.invocations.createAndWait(
4 runtime.id,
5 {
6 action: "extract",
7 instruction: "Extract the invoice total.",
8 schema: z.object({ total: z.number() }),
9 },
10 { timeoutMs: 60_000 }
11);
12
13console.log(invocation.status, invocation.output);

Pass a Zod schema as schema and the SDK converts it to JSON Schema on the wire and validates the output. The timeoutMs option is your client-side wait budget across long-poll requests.

Actions

Every invocation has an action. The lower-level create takes the action directly:

1const inv = await bctrl.runtimes.invocations.create(runtime.id, {
2 action: "act",
3 instruction: "Click the export button.",
4});
ActionWhat it does
actPerform one natural-language action on the page.
observeInspect the page and return Stagehand guidance.
extractPull structured data, optionally against a schema.
stagehandAgentRun a multi-step Stagehand agent flow.
browserUseRun a browser-use agent task.
solveCaptchaSolve a CAPTCHA on the active page.

Typed helpers

Stagehand and browser-use actions have typed helpers so you do not write the action field by hand:

1// Stagehand
2await bctrl.runtimes.invocations.stagehand.act(runtime.id, {
3 instruction: "Click the export button.",
4});
5
6await bctrl.runtimes.invocations.stagehand.extract(runtime.id, {
7 instruction: "Extract the invoice total.",
8 schema: z.object({ total: z.number() }),
9});
10
11await bctrl.runtimes.invocations.stagehand.agent(runtime.id, {
12 instruction: "Download every invoice from the last quarter.",
13 maxSteps: 30,
14});
15
16// browser-use
17await bctrl.runtimes.invocations.browserUse.agent(runtime.id, {
18 instruction: "Find the cheapest flight and add it to the cart.",
19 maxSteps: 50,
20});

Helpers accept a saved AI provider (aiProviderId), toolsets (toolsetId, toolIds), and per-call knobs like model and temperature.

Create, then wait separately

For long-running agents, create now and await later - or cancel:

1const inv = await bctrl.runtimes.invocations.create(runtime.id, {
2 action: "browserUse",
3 instruction: "Reconcile this month's transactions.",
4});
5
6const result = await bctrl.runtimes.invocations.wait(runtime.id, inv.id, {
7 timeoutMs: 30_000,
8});
9
10// or stop it
11await bctrl.runtimes.invocations.cancel(runtime.id, inv.id);

Read invocation history

Invocations belong to a run. Read them from the run side:

1const { data } = await bctrl.runs.invocations.list(runId);
2const one = await bctrl.runs.invocations.get(runId, inv.id);

Next