Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Ready-to-use automation templates for Stagehand and Browserbase. Each template h
| Template | TS | PY | GO | Description |
| -------------------------------- | ------------------------------------------------- | --------------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------- |
| agent-with-human-in-loop | [TS](typescript/agent-with-human-in-loop) | - | - | Build an AI agent that can pause and ask a human for input mid-task |
| agentskit-browser-agent | [TS](typescript/agentskit-browser-agent) | - | - | Run a provider-portable AgentsKit agent against a recorded Browserbase cloud browser session |
| amazon-global-price-comparison | [TS](typescript/amazon-global-price-comparison) | [PY](python/amazon-global-price-comparison) | - | Compare Amazon product prices across multiple countries using geolocation proxies |
| amazon-product-scraping | [TS](typescript/amazon-product-scraping) | [PY](python/amazon-product-scraping) | - | Scrape the first 3 Amazon search results for a given query and return structured product data |
| basic-caching | [TS](typescript/basic-caching) | [PY](python/basic-caching) | - | Demonstrate how Stagehand's caching feature reduces cost and latency by reusing previously computed actions |
Expand Down
8 changes: 8 additions & 0 deletions typescript/agentskit-browser-agent/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
BROWSERBASE_API_KEY=your_browserbase_api_key
OPENROUTER_API_KEY=your_openrouter_api_key

# Optional: replace this with any OpenRouter model without changing the agent.
OPENROUTER_MODEL=openrouter/free

# Optional: replace the task while keeping the same runtime and browser tools.
AGENT_TASK=Open example.com and report its page heading.
2 changes: 2 additions & 0 deletions typescript/agentskit-browser-agent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
node_modules/
55 changes: 55 additions & 0 deletions typescript/agentskit-browser-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# AgentsKit Browser Agent on Browserbase

## Introduction

- **Goal**: run an inspectable AgentsKit agent against a recorded Browserbase cloud browser session.
- **Pattern**: Browserbase session → Playwright over CDP → six-method AgentsKit browser contract → bounded agent run.
- **Provider-portable**: the example defaults to OpenRouter's free router, and the model can be replaced through one environment variable. The browser integration does not depend on the model provider.
- **Auditable**: Browserbase records the session, while AgentsKit returns the answer, step count, and tool calls.

## Quickstart

1. `cd typescript/agentskit-browser-agent`
2. `npm install`
3. `cp .env.example .env`
4. Add `BROWSERBASE_API_KEY` and `OPENROUTER_API_KEY` to `.env`
5. `npm start`

The default task opens `example.com` and reports its heading. Change `AGENT_TASK` to run another browser task. Keep tasks scoped to sites you are authorized to automate.

## Expected output

- A Browserbase session replay URL
- A grounded answer produced from the remote page
- The number of agent steps and browser tool calls

## How it works

`createBrowserPage()` maps the Browserbase Playwright page to AgentsKit's small browser contract: navigate, click, fill, read, screenshot, and wait. `browserAgent()` exposes those capabilities as model-callable tools. The runtime limits execution to eight steps and closes the remote browser in a `finally` block.

This separation keeps both sides replaceable:

- Switch the LLM by changing `OPENROUTER_MODEL`, or replace the AgentsKit adapter.
- Switch browser infrastructure by providing any Playwright- or Puppeteer-compatible page with the same six methods.
- Add AgentsKit memory, RAG, observability, or approval policies without rewriting the browser adapter.

## Validation

- `npm test` verifies the complete Playwright-to-AgentsKit method mapping without credentials or a live browser.
- `npm run typecheck` checks the template in TypeScript strict mode.
- `npm start` performs the live Browserbase and model-provider validation.

## Common pitfalls

- Missing key: both `BROWSERBASE_API_KEY` and `OPENROUTER_API_KEY` are required for the live example.
- Free model capacity: `openrouter/free` may be rate-limited; set `OPENROUTER_MODEL` to another model when needed.
- Selector failure: the model can only use selectors that exist on the current page. Use the Browserbase replay to inspect failures.
- Long-running tasks: the runtime intentionally stops after eight steps. Increase `maxSteps` only for a clearly bounded workflow.

## Resources

- [AgentsKit](https://www.agentskit.io)
- [AgentsKit browser tools](https://www.agentskit.io/docs/agents/tools/integrations/browser-agent)
- [Browserbase Playwright quickstart](https://docs.browserbase.com/welcome/quickstarts/playwright)
- [Browserbase session inspector](https://docs.browserbase.com/platform/browser/observability/session-recording)
- [OpenRouter models](https://openrouter.ai/models)
21 changes: 21 additions & 0 deletions typescript/agentskit-browser-agent/browser-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { BrowserPage } from "@agentskit/tools/integrations";
import type { Page } from "playwright-core";

export function createBrowserPage(page: Page): BrowserPage {
return {
goto: async (url) => {
await page.goto(url, { waitUntil: "domcontentloaded" });
},
click: async (selector) => {
await page.click(selector);
},
fill: async (selector, value) => {
await page.fill(selector, value);
},
textContent: async (selector) => (await page.textContent(selector)) ?? "",
screenshot: async () => (await page.screenshot({ type: "png" })).toString("base64"),
waitForSelector: async (selector, options) => {
await page.waitForSelector(selector, { timeout: options?.timeoutMs });
},
};
}
62 changes: 62 additions & 0 deletions typescript/agentskit-browser-agent/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { openrouter } from "@agentskit/adapters";
import { createRuntime, type RuntimeConfig } from "@agentskit/runtime";
import { browserAgent } from "@agentskit/tools/integrations";
import Browserbase from "@browserbasehq/sdk";
import "dotenv/config";
import { chromium } from "playwright-core";
import { createBrowserPage } from "./browser-page.js";

function required(name: "BROWSERBASE_API_KEY" | "OPENROUTER_API_KEY"): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing ${name}. Copy .env.example to .env and add the key.`);
}
return value;
}

async function main(): Promise<void> {
const browserbaseApiKey = required("BROWSERBASE_API_KEY");
const openrouterApiKey = required("OPENROUTER_API_KEY");
const model = process.env.OPENROUTER_MODEL ?? "openrouter/free";
const task = process.env.AGENT_TASK ?? "Open example.com and report its page heading.";

const bb = new Browserbase({ apiKey: browserbaseApiKey });
const session = await bb.sessions.create();
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0]?.pages()[0];

if (!page) {
await browser.close();
throw new Error("Browserbase session did not provide a page.");
}

console.log(`Session replay: https://browserbase.com/sessions/${session.id}`);

try {
const tools = browserAgent({ page: createBrowserPage(page) }) as unknown as NonNullable<
RuntimeConfig["tools"]
>;
const runtime = createRuntime({
adapter: openrouter({
apiKey: openrouterApiKey,
model,
}),
tools,
systemPrompt:
"Use the browser tools to complete the task. Prefer reading the page over guessing, and report only what you can verify.",
maxSteps: 8,
});

const result = await runtime.run(task);
console.log(result.content);
console.log(`Completed in ${result.steps} steps with ${result.toolCalls.length} tool calls.`);
} finally {
await browser.close();
}
}

main().catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
console.error(`Error: ${message}`);
process.exitCode = 1;
});
Loading