Skip to content

Option A: cloud browser via snippet, drop browser_open_cloud tool#42

Closed
Alezander9 wants to merge 1 commit intofeat/phase-h-ts-harnessfrom
feat/option-a-browser-tool
Closed

Option A: cloud browser via snippet, drop browser_open_cloud tool#42
Alezander9 wants to merge 1 commit intofeat/phase-h-ts-harnessfrom
feat/option-a-browser-tool

Conversation

@Alezander9
Copy link
Copy Markdown
Member

Stacked on top of #41 (feat/phase-h-ts-harness). Implements Option A from the PR #41 review thread.

Why

The local-browser connect path is already snippet-side (the agent calls await session.connect(...) itself in a browser_execute snippet — picks Way 1 or Way 2 from BROWSER.md). browser_open_cloud was the only opaque path: it wrapped one HTTP call and hid the Browser Use API surface from the agent. That asymmetry was the root of:

  • Cubic's P2 lifecycle finding ("tool description claims cloud browsers are stopped at bcode session end, but current implementation notes they usually persist until process exit because session eviction is not currently called") — true symptom of a wrapper that pretends to manage state but actually can't.
  • The broader "two patterns mixed" review note on PR phase-h: TS harness port (v0.1.0 cut) #41.

This commit makes cloud match local. Agent provisions / connects / stops / swaps cloud browsers from inside browser_execute snippets using fetch against https://api.browser-use.com/api/v3/browsers, exactly the same way it calls await session.connect({ profileDir }) for a local browser.

Decision recorded as decisions.md §3.10 (Rev 7) on the agent side.

What's removed

File LOC
packages/bcode-browser/src/cloud-browser.ts -109
packages/opencode/src/tool/browser-open-cloud.ts -35
packages/opencode/src/tool/browser-open-cloud.txt -12
Registry hookup (4 lines in tool/registry.ts) -4
SessionStore.onEvict + cleanup-callback machinery + Entry type wrapper ~-25

SessionStore.evict(sessionID) is preserved (with simpler signature: just close + delete) because test/browser-execute.test.ts uses it for between-case cleanup.

What's added

  • packages/bcode-browser/skills/cloud-browser.md (~150 LOC). The Way 3 deep dive: provision (POST), connect, stop (PATCH), swap (stop + close + provision again). Recommends a reusable ./.bcode/agent-workspace/cloud.ts workspace helper for projects that use cloud browsers more than once. Documents process.env.BROWSER_USE_API_KEY as the auth source — see "Auth shape" below.
  • Discovery of cloud-browser.md flows from browser-execute.txtBROWSER.mdcloud-browser.md (read on demand, exactly the pattern the user asked for in the previous review pass).

What's updated

  • packages/bcode-browser/skills/BROWSER.md rewritten around Way 1 / Way 2 / Way 3 (the user's running Chrome via popup-gated remote debugging / isolated debug-port Chrome / Browser Use cloud). New "Switching browsers mid-session" section documents the await session.close(); await session.connect(...) pattern. New listability hint: read {{SKILLS_DIR}}/interaction-skills/ to enumerate skills without committing to read.
  • packages/opencode/src/tool/browser-execute.txt points at cloud-browser.md for cloud specifics and notes process.env is available in snippet scope.
  • packages/bcode-browser/{README.md, package.json, src/index.ts} and a comment in src/browser-execute.ts: cloud-browser.ts references removed; brief note that cloud is intentionally not its own Level-1 surface.

Auth shape

BROWSER_USE_API_KEY stays as an environment variable. The agent reads it via process.env.BROWSER_USE_API_KEY from inside a snippet.

opencode's auth.json provider system was considered and rejected: it's plugin-driven and tied to the LLM-provider abstraction. Treating "browser-use" as a provider would force us into the plugin/provider system for one API key, drag a Yellow-zone modification into packages/opencode/, and confuse the user about whether bcode is selecting Browser Use as a model. Env var matches the existing AWS Bedrock pattern in opencode and keeps everything in Green zone (additive in bcode-browser/).

Net LOC

LOC
Hand-written removed ~-200
Markdown added (cloud-browser.md) +150
Modified comments / prompt rewrites wash
Net hand-written maintenance surface ~-50

The bigger win is conceptual:

  1. One tool surface (browser_execute) — back to the original Phase H §3.2 single-tool target.
  2. Zero wrapper-PR treadmill on BU API growth. Profile sync, custom proxies, regional pools, recording on/off, and any future BU API knob is reachable from a snippet without a bcode-side wrapper PR.
  3. Cubic P2 finding closed: no tool claims cloud auto-stop, so there's no false claim. Stop is the agent's responsibility, documented explicitly in cloud-browser.md's Stop section. Hard rule extension: the migration plan's "workspace as plain code, no privileged files" extends to "no privileged tools either."

Verification

  • bun typecheck clean across all browsercode packages (@browser-use/{browsercode-core,bcode-browser,bcode-laminar}, @opencode-ai/{core,plugin,sdk}) via the turbo-filtered root run.
  • bun test from packages/bcode-browser/: 4 pass (workspace-import always-on), 5 skip (chrome-gated under BCODE_SMOKE_CHROME=1 — same as PR phase-h: TS harness port (v0.1.0 cut) #41's smoke matrix).
  • embed-skills.ts now embeds 18 files (was 17 = BROWSER.md + 16 interactions; +1 for cloud-browser.md). Verified locally.

Stacking

Branch is feat/option-a-browser-tool based on feat/phase-h-ts-harness. Per AGENTS.md stacked-PR policy: PR #41 merges to main first; then this branch rebases onto main; then this PR retargets main and lands separately. v0.1.0 tag waits until both have landed on main.

If you'd rather land this folded into PR #41 instead of stacked, I can git rebase --onto feat/phase-h-ts-harness and force-push #41's branch with this commit appended — your call.

The local-browser connect path is already snippet-side (the agent calls
`await session.connect(...)` itself in a snippet, picks Way 1 / Way 2).
`browser_open_cloud` was the only opaque path — it wrapped one HTTP call
and hid the Browser Use API surface from the agent. That asymmetry was
the root of the cubic P2 lifecycle finding (auto-stop wired but never
invoked) and the broader "two patterns mixed" review feedback on PR #41.

This commit makes cloud match local. The agent provisions, connects,
stops, and swaps cloud browsers from inside `browser_execute` snippets
using `fetch` against `https://api.browser-use.com/api/v3/browsers`.

Removed:
- packages/bcode-browser/src/cloud-browser.ts (-109)
- packages/opencode/src/tool/browser-open-cloud.{ts,txt} (-47)
- registry hookup for BrowserOpenCloudTool
- SessionStore.onEvict + cleanup-callback machinery (~ -25 LOC,
  including the Entry wrapper type). evict() now just closes + deletes;
  kept because the test file uses it for cleanup between cases.

Added:
- packages/bcode-browser/skills/cloud-browser.md (~150 LOC). Way 3
  documentation: provision/connect/stop/swap, plus a recommended
  reusable workspace helper (.bcode/agent-workspace/cloud.ts) for
  projects that use cloud browsers more than once. BU API auth via
  process.env.BROWSER_USE_API_KEY (stays as an env var; opencode's
  auth.json was considered and rejected — see decisions.md §3.10).

Updated:
- BROWSER.md restructured around Way 1 / Way 2 / Way 3 (real Chrome
  popup-gated / isolated debug-port / cloud). New "Switching browsers
  mid-session" section. Listability hint added (`read
  {{SKILLS_DIR}}/interaction-skills/` to enumerate without committing).
  Removed the misleading "first call connects automatically" line —
  agent always calls connect() explicitly, just sometimes with no args.
- browser-execute.txt mentions cloud-browser.md and notes process.env
  is in snippet scope.
- bcode-browser README + package.json + index.ts: cloud-browser.ts
  references removed; brief note that cloud is intentionally not its
  own Level-1 surface.
- comments in browser-execute.ts: no more `browser_open_cloud`
  cross-references.

Net LOC: -200 deletions vs +150 markdown additions = ~-50 net hand-
written. Bigger win is one fewer tool surface (back to the original
Phase H §3.2 single-tool target) and zero wrapper-PR treadmill as the
BU API surface grows (profile sync, custom proxies, regional pools,
recording, etc. all reachable from a snippet without bcode changes).

Verification:
- bun typecheck clean across all browsercode packages.
- bun test from packages/bcode-browser/: 4 pass + 5 skip (chrome-gated).
- embed-skills.ts now embeds 18 files (was 17, +1 for cloud-browser.md).

Cubic P2 finding closed: no tool claims cloud auto-stop, so there is
no false claim. Stop is the agent's responsibility, documented in
cloud-browser.md "Stop".

Stacked on feat/phase-h-ts-harness; PR-A (#41) lands first, then this
rebases onto main and lands separately. v0.1.0 tag waits for both.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 12 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/bcode-browser/skills/cloud-browser.md">

<violation number="1" location="packages/bcode-browser/skills/cloud-browser.md:54">
P2: Handle the case where no page target is found before calling `session.use` to avoid a runtime crash.</violation>

<violation number="2" location="packages/bcode-browser/skills/cloud-browser.md:64">
P2: Validate the stop API response (`r.ok`) so failed stop requests don't silently leave cloud browsers running.</violation>
</file>

<file name="packages/bcode-browser/skills/BROWSER.md">

<violation number="1" location="packages/bcode-browser/skills/BROWSER.md:50">
P2: The new cloud connect example only reads `cdp_url`, even though this doc says some regions return `cdpUrl`. The snippet should accept both so copy-paste usage does not fail.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

When you're done, stop the browser. BU's quotas and idle reclaim will eventually clean it up if you forget, but explicit stop is faster and frees the slot:

```js
await fetch(`https://api.browser-use.com/api/v3/browsers/${id}`, {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 8, 2026

Choose a reason for hiding this comment

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

P2: Validate the stop API response (r.ok) so failed stop requests don't silently leave cloud browsers running.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/bcode-browser/skills/cloud-browser.md, line 64:

<comment>Validate the stop API response (`r.ok`) so failed stop requests don't silently leave cloud browsers running.</comment>

<file context>
@@ -0,0 +1,145 @@
+When you're done, stop the browser. BU's quotas and idle reclaim will eventually clean it up if you forget, but explicit stop is faster and frees the slot:
+
+```js
+await fetch(`https://api.browser-use.com/api/v3/browsers/${id}`, {
+  method: "PATCH",
+  headers: { "X-Browser-Use-API-Key": apiKey, "Content-Type": "application/json" },
</file context>
Fix with Cubic

await session.connect({ wsUrl: cdpUrl })
const targets = (await session.Target.getTargets({})).targetInfos
const page = targets.find(t => t.type === "page")
await session.use(page.targetId)
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 8, 2026

Choose a reason for hiding this comment

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

P2: Handle the case where no page target is found before calling session.use to avoid a runtime crash.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/bcode-browser/skills/cloud-browser.md, line 54:

<comment>Handle the case where no page target is found before calling `session.use` to avoid a runtime crash.</comment>

<file context>
@@ -0,0 +1,145 @@
+await session.connect({ wsUrl: cdpUrl })
+const targets = (await session.Target.getTargets({})).targetInfos
+const page = targets.find(t => t.type === "page")
+await session.use(page.targetId)
+```
+
</file context>
Fix with Cubic

Comment on lines +50 to +51
const { id, cdp_url, live_url } = await r.json()
await session.connect({ wsUrl: cdp_url })
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 8, 2026

Choose a reason for hiding this comment

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

P2: The new cloud connect example only reads cdp_url, even though this doc says some regions return cdpUrl. The snippet should accept both so copy-paste usage does not fail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/bcode-browser/skills/BROWSER.md, line 50:

<comment>The new cloud connect example only reads `cdp_url`, even though this doc says some regions return `cdpUrl`. The snippet should accept both so copy-paste usage does not fail.</comment>

<file context>
@@ -1,28 +1,62 @@
+  headers: { "X-Browser-Use-API-Key": process.env.BROWSER_USE_API_KEY, "Content-Type": "application/json" },
+  body: "{}",
+})
+const { id, cdp_url, live_url } = await r.json()
+await session.connect({ wsUrl: cdp_url })
+console.log("liveUrl for the user to watch:", live_url)
</file context>
Suggested change
const { id, cdp_url, live_url } = await r.json()
await session.connect({ wsUrl: cdp_url })
const { cdp_url, cdpUrl, live_url } = await r.json()
const wsUrl = cdp_url ?? cdpUrl
if (!wsUrl) throw new Error("Browser Use response missing cdp_url/cdpUrl")
await session.connect({ wsUrl })
Fix with Cubic

@Alezander9
Copy link
Copy Markdown
Member Author

Folded into PR #41 as commit c5ea60c per admin request — easier to review holistically.

@Alezander9 Alezander9 closed this May 8, 2026
@Alezander9 Alezander9 deleted the feat/option-a-browser-tool branch May 8, 2026 23:17
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