Skip to main content

Overview

Stagehand agents support streaming execution, allowing you to receive real-time updates as the agent performs actions. This is particularly useful for:
  • Providing live feedback to users
  • Monitoring long-running automation tasks
  • Debugging agent behavior
  • Building interactive UIs

Basic Streaming Example

Here’s how to create a streaming agent and consume its output:
import { Stagehand } from "@stagehand/core";
import chalk from "chalk";

async function main() {
  console.log(`\n${chalk.bold("Stagehand 🤘 Agent Streaming Example")}\n`);
  
  // Initialize Stagehand
  const stagehand = new Stagehand({
    env: "LOCAL",
    verbose: 0,
    cacheDir: "stagehand-agent-cache",
    logInferenceToFile: false,
    experimental: true,
  });

  await stagehand.init();

  try {
    const page = stagehand.context.pages()[0];
    await page.goto("https://amazon.com");

    // Create a streaming agent with stream: true in the config
    const agent = stagehand.agent({
      model: "anthropic/claude-sonnet-4-5-20250929",
      stream: true, // This makes execute() return AgentStreamResult
    });

    const agentRun = await agent.execute({
      instruction: "go to amazon, and search for shampoo, stop after searching",
      maxSteps: 20,
    });
    
    // Stream the text
    for await (const delta of agentRun.textStream) {
      process.stdout.write(delta);
    }
    
    const finalResult = await agentRun.result;
    console.log("Final Result:", finalResult);
  } catch (error) {
    console.log(`${chalk.red("✗")} Error: ${error}`);
  }
}

main();

Streaming Full Events

You can also stream complete events including tool calls and messages:
import { Stagehand } from "@stagehand/core";

async function streamFullEvents() {
  const stagehand = new Stagehand({
    env: "LOCAL",
    verbose: 0,
    experimental: true,
  });

  await stagehand.init();

  const page = stagehand.context.pages()[0];
  await page.goto("https://amazon.com");

  const agent = stagehand.agent({
    model: "anthropic/claude-sonnet-4-5-20250929",
    stream: true,
  });

  const agentRun = await agent.execute({
    instruction: "search for wireless headphones and show me the top result",
    maxSteps: 20,
  });

  // Stream everything (tool calls, messages, etc.)
  for await (const delta of agentRun.fullStream) {
    console.log(delta);
  }

  const finalResult = await agentRun.result;
  console.log("Final Result:", finalResult);
  
  await stagehand.close();
}

streamFullEvents();

Stream Result Types

When stream: true is enabled, agent.execute() returns an AgentStreamResult object with:

textStream

Streams text deltas as they’re generated:
for await (const delta of agentRun.textStream) {
  process.stdout.write(delta); // Print incrementally
}

fullStream

Streams complete events including:
  • Tool calls
  • Messages
  • Intermediate results
  • Status updates
for await (const event of agentRun.fullStream) {
  console.log(event.type, event.data);
}

result

A promise that resolves to the final result:
const finalResult = await agentRun.result;
console.log("Complete:", finalResult);

Streaming with Custom Tools

You can combine streaming with custom tools:
import { z } from "zod";
import { tool } from "ai";
import { Stagehand } from "@stagehand/core";

const searchProducts = tool({
  description: "Search for products in a database",
  inputSchema: z.object({
    query: z.string(),
  }),
  execute: async ({ query }) => {
    // Your search logic
    return { results: ["Product 1", "Product 2"] };
  },
});

async function streamWithTools() {
  const stagehand = new Stagehand({
    env: "LOCAL",
    experimental: true,
  });
  await stagehand.init();

  const page = stagehand.context.pages()[0];
  await page.goto("https://example-store.com");

  const agent = stagehand.agent({
    stream: true,
    tools: {
      searchProducts,
    },
  });

  const agentRun = await agent.execute({
    instruction: "Search for 'laptop' and show me the results",
    maxSteps: 20,
  });

  for await (const delta of agentRun.textStream) {
    process.stdout.write(delta);
  }

  const result = await agentRun.result;
  console.log("\nFinal:", result);
  
  await stagehand.close();
}

streamWithTools();

Streaming with CUA

Streaming works with Computer Use Agents:
import { Stagehand } from "@stagehand/core";

async function streamCUA() {
  const stagehand = new Stagehand({
    env: "LOCAL",
    verbose: 0,
    experimental: true,
  });
  await stagehand.init();

  const page = stagehand.context.pages()[0];

  const agent = stagehand.agent({
    mode: "cua",
    model: {
      modelName: "anthropic/claude-sonnet-4-5-20250929",
      apiKey: process.env.ANTHROPIC_API_KEY,
    },
    stream: true,
  });

  await page.goto("https://www.google.com");

  const agentRun = await agent.execute({
    instruction: "Search for 'Stagehand documentation' and click the first result",
    maxSteps: 20,
  });

  for await (const delta of agentRun.textStream) {
    process.stdout.write(delta);
  }

  const result = await agentRun.result;
  console.log("\nComplete:", result);
  
  await stagehand.close();
}

streamCUA();

Building a Progress UI

Here’s an example of using streaming to build a progress indicator:
import { Stagehand } from "@stagehand/core";
import chalk from "chalk";

async function withProgressUI() {
  const stagehand = new Stagehand({
    env: "LOCAL",
    experimental: true,
  });
  await stagehand.init();

  const page = stagehand.context.pages()[0];
  await page.goto("https://amazon.com");

  const agent = stagehand.agent({
    model: "anthropic/claude-sonnet-4-5-20250929",
    stream: true,
  });

  const agentRun = await agent.execute({
    instruction: "Find and add wireless earbuds to cart",
    maxSteps: 30,
  });

  let stepCount = 0;
  console.log(chalk.blue("🤖 Agent working...\n"));

  for await (const event of agentRun.fullStream) {
    if (event.type === "tool-call") {
      stepCount++;
      console.log(chalk.yellow(`Step ${stepCount}: ${event.data.toolName}`));
    } else if (event.type === "text-delta") {
      process.stdout.write(chalk.gray(event.data));
    }
  }

  const result = await agentRun.result;
  console.log(chalk.green("\n\n✓ Task complete!"));
  console.log("Result:", result);
  
  await stagehand.close();
}

withProgressUI();

Error Handling with Streams

Handle errors when streaming:
import { Stagehand } from "@stagehand/core";

async function streamWithErrorHandling() {
  const stagehand = new Stagehand({
    env: "LOCAL",
    experimental: true,
  });
  await stagehand.init();

  try {
    const page = stagehand.context.pages()[0];
    await page.goto("https://example.com");

    const agent = stagehand.agent({
      stream: true,
    });

    const agentRun = await agent.execute({
      instruction: "Complete a complex task",
      maxSteps: 20,
    });

    try {
      for await (const delta of agentRun.textStream) {
        process.stdout.write(delta);
      }

      const result = await agentRun.result;
      console.log("Success:", result);
    } catch (streamError) {
      console.error("Stream error:", streamError);
    }
  } catch (error) {
    console.error("Agent error:", error);
  } finally {
    await stagehand.close();
  }
}

streamWithErrorHandling();

Key Concepts

Enable Streaming

Set stream: true in the agent configuration:
const agent = stagehand.agent({
  stream: true, // Enable streaming
});

Async Iterators

Both textStream and fullStream are async iterators:
for await (const item of stream) {
  // Process each item as it arrives
}

Result Promise

The result property is a promise that resolves when execution completes:
const finalResult = await agentRun.result;

Best Practices

  1. Use textStream for simple output - Best for displaying progress to users
  2. Use fullStream for detailed monitoring - Access all events for debugging
  3. Handle errors in streams - Wrap streaming logic in try/catch
  4. Await the result - Always await result to get the final output
  5. Close resources - Call stagehand.close() when done
  6. Disable verbose logging - Set verbose: 0 to avoid cluttering stream output

Configuration Options

const stagehand = new Stagehand({
  env: "LOCAL",
  verbose: 0, // Disable logs for cleaner streaming
  cacheDir: "stagehand-agent-cache",
  logInferenceToFile: false, // Disable file logging
  experimental: true, // Required for streaming
});

Next Steps

Build docs developers (and LLMs) love