Skip to content

feat(agents): add a pi-durable browserTool for a persistent browser - #2484

Open
ben-reitz wants to merge 5 commits into
mainfrom
feat/pi-browser-tool
Open

ben-reitz wants to merge 5 commits into
mainfrom
feat/pi-browser-tool

Conversation

@ben-reitz

@ben-reitz ben-reitz commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Adds browserTool to a new agents/browser/pi entry point, so a pi-durable harness (agents/harness/pi) can drive the same persistent Browser as the AI SDK and TanStack AI tools.

import { browserTool } from "agents/browser/pi";

this.registry.install({
  name: "browser",
  tools: [browserTool({ ctx: this.ctx, browser: this.browser, loader: this.env.LOADER })]
});

It returns a pi ToolRegistration built on the shared createBrowserToolCore, so tabs, logins, sessionId: "active", newTabs, and restarted: true work as they do in the other tools.

What's different in pi

  • Screenshots come back as images. The result is a JSON text part (the trimmed model output, no calls) plus an image part. pi-ai sends the image only to models that accept images.
  • executionMode: "sequential": calls share the active tab, so they don't interleave.
  • replay: "unsafe": the code may have clicked or submitted something, so after an eviction pi gives the model an interrupted result instead of running it again.
  • ctx is passed explicitly when the host is a plain Durable Object, which is what the pi examples use.
  • details carries executionId, status, restarted, and newTabs for a UI.

Known gap: codemode ignores pi's abort signal, so stopping a conversation doesn't cancel a browser call already running. The call finishes or hits timeoutMs. The docs say so.

Tests

  • Unit tests next to the TanStack ones in browser-capability.test.ts: name, mode, replay, restart, image, oversized screenshot, error.
  • An end-to-end test through a real PiHarness with pi's faux model (harness/pi/tests/browser.test.ts). It checks that pi accepts the schema, runs the call, and stores the image in the transcript.
  • A type test in tests-d/browser-tool.test-d.ts.

Docs: a pi paragraph in "Persistent browser" (browse-the-web.md) and a Browser subsection in harnesses/pi.md. Changeset: agents minor.

The Pi example gets the tool in the PR stacked above this one.

@ben-reitz
ben-reitz added this pull request to stack #2486 October 5, 2026 13:46
@changeset-bot

changeset-bot Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 60471ee

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
agents Minor
@cloudflare/agent-think Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@agent-think

agent-think Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

🟢 agents import sizes: 1 entry point changed, no growth

Entry point Exports Largest gzip change Size now
🆕 agents/browser/pi 1 new — 76.3 KiB
Changed exports (1)
Import Gzip change Size now
🆕 agents/browser/pi#browserTool — 76.3 KiB
How this works

Each runtime export is bundled on its own, minified, and gzipped. Changes smaller than 100 B, or smaller than 1% and 1 KiB, are ignored. Growth over 10% or 5 KiB is marked 🔴. This report is informational and does not fail CI. The workflow artifact contains every measurement.

Compared cf7c9e3c → 60471eee · workflow run · reported by agent-think[bot]

@ben-reitz ben-reitz changed the title feat/pi browser tool feat(agents): add a pi-durable browserTool for a persistent browser Oct 5, 2026
@pkg-pr-new

pkg-pr-new Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@2484

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@2484

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@2484

hono-agents

npm i https://pkg.pr.new/hono-agents@2484

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@2484

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@2484

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@2484

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@2484

commit: 60471ee

@ben-reitz
ben-reitz removed this pull request from stack #2486 October 5, 2026 15:11
@ben-reitz
ben-reitz marked this pull request as ready for review October 6, 2026 12:37

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

description: core.description,
parameters: browserToolParameters,
// Calls share the active tab, so one round's calls must not interleave.
executionMode: "sequential",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Concurrent conversations lose their active tab

When separate conversations call browserTool concurrently, executionMode does not serialize them across conversations. Both save to the same browser's activeTargetId, so a later call can act on another conversation's tab.

Learn more

Pi runs different conversations concurrently; executionMode: "sequential" orders calls within a conversation's tool round, not calls from other conversations. BrowserSessionConnector saves the active target to a single browser record after each execution. The next execution resolves "active" from that shared record via activeTarget. Two conversations using one Browser can therefore switch one another's active tab even though each tool reports sequential execution.

Example: Conversation A creates tab A, and conversation B creates tab B before A's next call. B's pass saves tab B as active. A's Runtime.evaluate with sessionId: "active" now runs in tab B, not tab A.

Recommended fix: Decide whether the browser is intended to be shared between conversations. If not, assign a distinct named Browser to each conversation. If it is shared, track the active target per conversation and pass the conversation identity into the connector; a global execution lock alone cannot retain separate active tabs between turns.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread docs/agents/browse-the-web.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant