phase-h: TS harness port (v0.1.0 cut)#41
Conversation
Step 1 — vendor browser-harness-js@95b7a22a CDP layer into packages/bcode-browser/src/cdp/ (session.ts, gen.ts, generated.ts, browser_protocol.json, js_protocol.json) + PROVENANCE.md. Initial copy only; subsequent edits diverge by design (Phase H hard rule #2 — no sync cadence, we own this from here on). Step 2 — rewrite packages/bcode-browser/src/browser-execute.ts to evaluate JS in-process via new AsyncFunction("session", code) against a per- opencode-session CDP Session singleton (closed via Effect.addFinalizer). console.log/error/warn/info monkey-patched around each snippet; restored in finally even on throw/timeout. Snippet scope binds only `session` plus standard JS globals — nothing auto-loaded (Phase H hard rule #3: workspace as plain code, no privileged files). Level-2 wrapper resolves the per-project workspace dir <projectDir>/.bcode/agent-workspace/ from InstanceState.context at execute-time; the impl mkdir's it on first call. Prompt rewritten around the no-magic model with the write-once-import-many pattern as the first example. Permission glob in agent.ts: harnessGlob/harnessArchiveGlob/ harnessArchiveEditDeny removed; replaced with project-relative **/.bcode/agent-workspace/**/* edit-allow. TUI rendering: switched from Python REPL prompts (">>> " / "... ") to JS prompts ("> " / " "); input.python -> input.code. Deleted: harness.ts, uv-locate.ts (no subprocess/uv path anymore). Typecheck clean across all browsercode packages.
Step 3 — packages/bcode-browser/test/workspace-import.test.ts. Four
scenarios verifying the BROWSER.md-documented pattern: import abs path,
edit-then-cache-bust, syntax-error rejection, nested workspace paths.
All four pass with bun test against real fs.
Step 4 — packages/bcode-browser/src/cloud-browser.ts (provisions BU cloud
browser via /api/v3/browsers, connects the per-opencode-session Session
to the cdpUrl, registers stop on session evict). Level-2 wrapper at
packages/opencode/src/tool/browser-open-cloud.{ts,txt} + registry
registration.
Restructured the Session lifecycle as a process-scope SessionStore keyed
on opencode sessionID. Both browser_execute and browser_open_cloud share
the same Session per session — a snippet that follows browser_open_cloud
drives the cloud browser, not a freshly-auto-detected local one. The
store also collects per-session cleanup callbacks (cloud-browser stop
via PATCH /api/v3/browsers/<id>); these run when SessionStore.evict is
called. opencode has no clean session-end hook today so evict is wired
but not yet invoked from outside — known gap matching today's subprocess
shape.
BROWSER_USE_API_KEY required; fail-fast with a one-line error pointing
at https://browser-use.com when absent.
- packages/bcode-browser/skills/ — browsercode-owned. BROWSER.md (the
agent's prompt for browser_execute, centered on the no-magic /
workspace-as-plain-code model) + interaction-skills/*.md (verbatim
initial copy from browser-harness-js@95b7a22a; ours after).
- packages/bcode-browser/script/embed-skills.ts — smaller cousin of the
retired embed-harness.ts. Walks skills/, emits bcode-skills.gen.ts
with file map + content-hash buildHash sentinel.
- packages/bcode-browser/src/skills.ts — runtime resolver. Dev: in-tree
path. Compiled: extracts to <dataDir>/skills/ once per build hash;
baseline-overwrite (no agent-editable surface; the agent's editable
surface is per-project <projectDir>/.bcode/agent-workspace/).
- packages/opencode/script/build.ts — swap createEmbeddedHarnessBundle
for createEmbeddedSkillsBundle; rename bcode-harness.gen.ts ->
bcode-skills.gen.ts in the Bun.build files map and entrypoints.
- packages/opencode/src/agent/agent.ts — drop harnessGlob/harnessArchive*
(already dropped in step 2); add browserSkillsGlob pointing at
Skills.skillsDir(Global.Path.data).
- packages/opencode/src/tool/browser-execute.{ts,txt} — substitute
{{SKILLS_DIR}} at make-time; prompt points at <skillsDir>/BROWSER.md
and <skillsDir>/interaction-skills/.
- packages/bcode-browser/{README.md,src/index.ts} — refreshed to reflect
the new contents; harness column gone.
- DELETE packages/bcode-browser/harness/ entirely (~3400 LOC; the
largest single deletion of the port). Net hand-written code drop from
this step alone is ~-2000 LOC even after adding ~600 LOC of TS for
cdp/, browser-execute.ts, cloud-browser.ts, skills.ts, session-store.ts,
embed-skills.ts.
- DELETE harness-sync.md (retired with the harness vendoring).
- DELETE script/check-harness-diff.sh; trim harness branch from
script/check-upstream.sh.
- UPSTREAM.md retitled to track only anomalyco/opencode; old harness
sync log preserved as historical archaeology.
- memory side: created memory/browsercode/harness_watchlist.md (Phase H
hard rule #2 mechanism — patterns to track for possible porting later,
no sync cadence).
Typecheck clean across all browsercode packages. workspace-import
smoke tests still pass.
Two new env-gated test files exercise the in-process CDP stack against
headless Chrome (Chrome 147, via google-chrome --headless=new):
- packages/bcode-browser/test/cdp-smoke.test.ts — connect via
profileDir, attach a target, Page.enable + navigate +
waitFor("Page.loadEventFired") + Runtime.evaluate("document.title").
Verifies the vendored CDP layer (session.ts + generated.ts) drives a
real browser end-to-end.
- packages/bcode-browser/test/browser-execute.test.ts — exercises the
full Level-1 execute path: AsyncFunction wrapping, console.log
capture, return-value serialization, multi-call Session reuse via
SessionStore (a follow-up snippet on the same sessionID sees the
Session previously connected), and workspace dynamic-import inside
a snippet (await import(absPath + "?t=" + Date.now())).
Both gated on BCODE_SMOKE_CHROME=1 + BCODE_SMOKE_PROFILE_DIR. Skipped
otherwise — CI does not yet drive a real browser. All 9 tests
(4 workspace-import + 1 cdp-smoke + 4 browser-execute) pass on
Linux x64.
Compiled binary smoke verified separately: bun build --compile
produces bcode-linux-x64 (148 MB) that boots cleanly, prints --help,
and runs without uv on PATH (the only dependency was the deleted
Python harness).
Cloud-browser tool not exercised here — requires BROWSER_USE_API_KEY
and outbound to api.browser-use.com. Defer to admin smoke or Phase D
bring-up.
Smoke matrix: Linux x64 ✓; macOS arm64 + Windows x64 pending
admin-laptop runs (recorded in memory/browsercode/phase_h_smoke_log.md
on the agent side).
There was a problem hiding this comment.
12 issues found across 96 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/interaction-skills/print-as-pdf.md">
<violation number="1" location="packages/bcode-browser/skills/interaction-skills/print-as-pdf.md:27">
P2: `headerTemplate`/`footerTemplate` placeholders are documented incorrectly; CDP expects class-based spans, not `{{...}}` variables.</violation>
</file>
<file name="packages/bcode-browser/skills/interaction-skills/iframes.md">
<violation number="1" location="packages/bcode-browser/skills/interaction-skills/iframes.md:69">
P2: `sandbox="allow-same-origin"` is described as a blocker, but it actually enables same-origin DOM access. This guidance can mislead users to debug the wrong cause of `contentDocument` failures.</violation>
</file>
<file name="packages/bcode-browser/skills/interaction-skills/connection.md">
<violation number="1" location="packages/bcode-browser/skills/interaction-skills/connection.md:72">
P2: Handle the empty `tabs` case in the startup sequence before accessing `tabs[0].targetId`, otherwise this snippet can throw on fresh browser starts with no real page targets.</violation>
</file>
<file name="packages/bcode-browser/skills/BROWSER.md">
<violation number="1" location="packages/bcode-browser/skills/BROWSER.md:72">
P3: The docs contradict themselves about workspace layout (`flat directory` vs `any depth/subdir`). Clarify this to avoid users organizing scripts incorrectly.</violation>
</file>
<file name="packages/bcode-browser/skills/interaction-skills/dropdowns.md">
<violation number="1" location="packages/bcode-browser/skills/interaction-skills/dropdowns.md:38">
P2: Guard `textContent` before calling `.trim()` to avoid a runtime TypeError in the dropdown option lookup example.</violation>
</file>
<file name="packages/bcode-browser/skills/interaction-skills/cross-origin-iframes.md">
<violation number="1" location="packages/bcode-browser/skills/interaction-skills/cross-origin-iframes.md:26">
P2: Guard the `find(...)` result before using `iframe.targetId` to avoid a runtime crash when no matching OOPIF target exists yet.</violation>
<violation number="2" location="packages/bcode-browser/skills/interaction-skills/cross-origin-iframes.md:35">
P2: Define how to resolve the parent target before calling `session.use(parentTargetId)`; the current example references an undefined variable.</violation>
</file>
<file name="packages/bcode-browser/skills/interaction-skills/shadow-dom.md">
<violation number="1" location="packages/bcode-browser/skills/interaction-skills/shadow-dom.md:11">
P2: The Shadow DOM guidance documents a non-existent CDP option (`pierceShadow` on `DOM.querySelector`/`DOM.querySelectorAll`). Use `DOM.getDocument(..., pierce: true)` / shadow-root traversal before querying.</violation>
</file>
<file name="packages/opencode/src/tool/browser-open-cloud.txt">
<violation number="1" location="packages/opencode/src/tool/browser-open-cloud.txt:3">
P2: The 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.</violation>
</file>
<file name="packages/bcode-browser/skills/interaction-skills/dialogs.md">
<violation number="1" location="packages/bcode-browser/skills/interaction-skills/dialogs.md:69">
P2: The `beforeunload` “Option A” snippet has a race: it tries to handle the dialog immediately after navigation without waiting for the dialog-open event, so it can miss late-opening dialogs.</violation>
</file>
<file name="packages/bcode-browser/src/cloud-browser.ts">
<violation number="1" location="packages/bcode-browser/src/cloud-browser.ts:103">
P1: Stop the provisioned cloud browser when `session.connect` fails; otherwise failed attach attempts can leak running cloud browser instances.</violation>
</file>
<file name="packages/bcode-browser/src/browser-execute.ts">
<violation number="1" location="packages/bcode-browser/src/browser-execute.ts:131">
P1: Global `console` monkey-patching is not concurrency-safe. If two `execute` calls overlap (different sessions), they clobber each other's patches — one call's output capture silently breaks. Consider a per-call capture approach, e.g. passing a custom logger object into the snippet as an argument (`log`) instead of mutating the global.</violation>
</file>
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.
Fix all with cubic
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.
|
Added a 5th commit folding Option A (was previously stacked as PR #42, now closed): This addresses the "two patterns mixed" review note and closes cubic's P2 lifecycle finding by deletion (no tool can claim auto-stop if there's no tool). What changed in this commit (12 files, +239 / -217, net hand-written ~-50 LOC): Removed:
Added:
Updated:
Auth shape: Verification:
Mental model after this commit:
|
There was a problem hiding this comment.
1 issue found across 12 files (changes from recent commits).
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/BROWSER.md">
<violation number="1" location="packages/bcode-browser/skills/BROWSER.md:50">
P2: The Way 3 quick-start snippet only reads `cdp_url`/`live_url`; it should accept both snake_case and camelCase response keys to avoid failed cloud connects in some regions.</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.
Fixes cubic P2 finding on PR #41. Two overlapping execute() calls clobbered each other's global console.log/error/warn/info patches and corrupted the originals process-wide via a stale 'finally' restore. Cases this hits: server-mode multi-session, sub-agent sessions, parallel browser_execute tool calls in one assistant turn (AI SDK runs those via Promise.all), TUI tabs sharing a daemon. Fix: bind a per-call { log, error, warn, info } object as the second AsyncFunction argument. JS scope chain resolves console.log() in the snippet to the function parameter before reaching the global, so existing snippets keep working byte-identically and the global console is never mutated. Also concurrency-safe by construction -- no shared state between calls. Test 'overlapping execute calls do not clobber each other's console capture' is a regression guard: verified to fail on the old impl (empty output captures + 'bye from B' leaking to stderr) and pass on the new one.
|
Cubic's concurrency-safety finding on Bug: Two overlapping
Net: B captures partial/nothing, A's output gets foreign log lines, the global console stays corrupted process-wide. Realistic concurrency modes that hit this: server-mode multi-session, sub-agent (Task tool) sessions, parallel Fix: Bind a per-call Regression test: Diff: 2 files, +68 / −29. |
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
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/src/browser-execute.ts">
<violation number="1" location="packages/bcode-browser/src/browser-execute.ts:136">
P2: The injected `console` only exposes four methods, which breaks snippets that use other standard console APIs (e.g. `console.debug`).</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic
Skills (factual corrections from cubic):
- print-as-pdf.md: headerTemplate/footerTemplate use class-tagged spans
(<span class="pageNumber"></span>), not mustache variables.
- iframes.md: sandbox=allow-same-origin ENABLES same-origin DOM access;
the blocker is sandbox WITHOUT it. Was documented backwards.
- connection.md: Startup sequence now handles tabs[]==[] (fresh window
with omnibox-only) by createTarget('about:blank') before session.use.
- dropdowns.md: guard textContent with ?? '' before .trim().
- cross-origin-iframes.md: define parentTargetId by querying Target.getTargets
before session.use(iframe.targetId); guard the find() result.
- shadow-dom.md: rewrite CDP path -- DOM.querySelector does NOT accept
pierceShadow. Use DOM.getDocument({pierce: true}) to fetch the flat
tree, walk shadowRoots[], then querySelector against the shadow-root
nodeId. The '>>>' combinator note removed (non-standard).
- dialogs.md: beforeunload Option A now subscribes via session.onEvent
before navigating, eliminating the race where the dialog opens before
handleJavaScriptDialog is called.
- BROWSER.md: Way 3 example now accepts both snake_case and camelCase
response keys (cdp_url/cdpUrl, live_url/liveUrl) -- BU returns either
depending on region. Workspace contradiction resolved: 'flat directory'
→ 'flat for small projects, subdirs when accumulation helps'.
browser-execute.ts (cubic console.debug finding):
- Snippet console now prototype-chains to the real console via
Object.create(console). console.debug is tee'd into capture; uncommon
methods (console.table, .dir, .trace, .group, …) fall through to the
real console without throwing TypeError. Added regression test.
All cubic comments on PR #41 now resolved.
|
Addressed remaining cubic findings in 7e72a0c. Summary: Factual corrections in skill docs (cubic was right on all of them):
Verification: Followup logged in roadmap. All these inherited skill drafts are not yet battle-tested; cubic surfaced ~10 factual errors in one read. Added Phase G7 to |
Phase H — TS harness port (v0.1.0 cut)
Drops
uvfrom the install path and replaces the Pythonbrowser-harnesssubprocess + daemon with an in-process TypeScript CDP layer. Implements the migration plan end-to-end (steps 0–6). Step 7 is "tag from main"; that's an admin call after merge.Net LOC (hand-written maintenance surface)
(Excludes
generated.ts~15k and the two protocol JSONs ~1.5 MB, which are committed-data, not maintenance surface —bun src/cdp/gen.tsregeneratesgenerated.tsfrom the JSONs.)The plan's target was −1,800 LOC; we beat it because the rewrite was tighter than budgeted (browser-execute.ts: 163 → ~125 LOC; harness.ts + uv-locate.ts: 217 LOC removed entirely).
What's in the box
Step 1 — vendor the CDP layer.
packages/bcode-browser/src/cdp/:session.ts(~380 LOC),gen.ts(~270 LOC,bun src/cdp/gen.tsto regenerate),generated.ts(~15k LOC machine-generated, committed),browser_protocol.json+js_protocol.jsonfromchromedevtools/devtools-protocol. Initial copy frombrowser-use/browser-harness-js@95b7a22a; ours from here on. Provenance recorded inpackages/bcode-browser/src/cdp/PROVENANCE.md. Theexport namespacecarve-out ingenerated.tsis documented inEXCEPTIONS.md(it's regenerated machine output, not hand-authored).Step 2 — in-process
browser_execute.packages/bcode-browser/src/browser-execute.ts(full rewrite). Wraps the snippet withnew AsyncFunction("session", code)against a per-opencode-sessionSessionfrom a process-scopeSessionStore. Snippet scope binds onlysessionplus standard JS globals — nothing auto-loaded (Phase H hard rule sync: upstream v1.14.22 (eb7555d3c on dev) #3, "workspace as plain code, no privileged files").console.log/error/warn/infomonkey-patched around each snippet, restored in afinallyeven on throw/timeout.packages/bcode-browser/src/session-store.ts— per-opencode-session CDPSessionmap, shared betweenbrowser_executeandbrowser_open_cloud. OptionalonEvictcleanup callbacks (cloud-browser stops register here).packages/opencode/src/tool/browser-execute.ts— Level-2 wrapper resolves<projectDir>/.bcode/agent-workspace/per-call fromInstanceState.context(opencode's existing project-detection — same source that finds.bcode/plans,.bcode/db, etc.).packages/opencode/src/tool/browser-execute.txt— new prompt centered on the no-magic / workspace-as-plain-code model. Substitutes{{SKILLS_DIR}}at make-time; workspace path is project-relative (./.bcode/agent-workspace/) and constructible fromprocess.cwd()inside a snippet.agent.tspermission glob: harness/archive entries gone; replaced with**/.bcode/agent-workspace/**/*(read+edit) plus the runtime skills tree at<dataDir>/skills/*.>>> /...Python-REPL prompts →> /JS prompts;input.python→input.code.harness.ts+uv-locate.ts.Step 3 — workspace dynamic-import smoke (
packages/bcode-browser/test/workspace-import.test.ts). Four cases, allbun testagainst realfs: import abs path, edit-then-cache-bust, syntax-error rejection, nested workspace paths. The point isn't to build a framework — it's to verify the documentedawait import("/abs?t=" + Date.now())pattern is honest about what it claims.Step 4 — cloud-browser tool (
browser_open_cloud).packages/bcode-browser/src/cloud-browser.ts— POST/api/v3/browsers→{id, cdpUrl, liveUrl}→Session.connect({ wsUrl: cdpUrl })→SessionStore.onEvictregisters the PATCH stop. Fails fast ifBROWSER_USE_API_KEYis absent.packages/opencode/src/tool/browser-open-cloud.{ts,txt}+ registry hook (one line).browser_open_cloud, every subsequentbrowser_executedrives the cloud browser via the sameSession(no per-snippet re-attach).Step 5 — skills, BROWSER.md, embed.
packages/bcode-browser/skills/BROWSER.md— browsercode-owned prompt for the agent's firstbrowser_executecall. Covers connect, target attach, common moves (Page.navigate, mouse,Input.insertText, screenshot), the workspace write-once-import-many pattern, and a guardrails block (no top-levelimport, no CPU-bound loops withoutawait, JSON-serializable returns).packages/bcode-browser/skills/interaction-skills/*.md— 16 UI-mechanic recipes (dialogs, dropdowns, iframes, shadow DOM, uploads, screenshots, …) initial-copied frombrowser-harness-js@95b7a22a. Read-only docs; the agent reads them withread, neverimports.packages/bcode-browser/script/embed-skills.ts(new) replacesembed-harness.tsinpackages/opencode/script/build.ts. Glob-driven, content-hash buildHash sentinel. Compiled binary extracts to<dataDir>/skills/once per buildHash; baseline-overwrite on every launch (no agent-editable surface here — the agent's editable surface is<projectDir>/.bcode/agent-workspace/).packages/bcode-browser/src/skills.ts(new) — runtime resolver. Dev: in-tree path. Compiled: extract on first call, stat-and-skip on warm launch.packages/bcode-browser/harness/(entire vendored Python tree),embed-harness.ts,harness-sync.md,script/check-harness-diff.sh. Trimmedscript/check-upstream.shto drop the harness branch.UPSTREAM.mdretitled to track onlyanomalyco/opencode; the old harness sync log is preserved as historical archaeology in §3.memory/browsercode/harness_watchlist.mdrecords Python-harness behaviors the agent might want to port to TS later, individually and on cadence-of-need (not a sync schedule). Hard rule f4: scope turbo typecheck to browsercode-packages filter #2 mechanism.Step 6 — cross-platform smoke.
bun build --compile→bcode-linux-x64(148 MB) boots cleanly withoutuv. Three test files, 9 cases: workspace-import (4) + cdp-smoke (1, attaches to real Chrome, navigates, readsdocument.title) + browser-execute (4, end-to-end against headless Chrome incl. workspace dynamic-import inside a snippet). All pass.memory/browsercode/phase_h_smoke_log.mdon the agent side.BROWSER_USE_API_KEYand outbound toapi.browser-use.com. Defer to admin smoke or Phase D bring-up.Hard rules honored
rm -rf ~/.local/share/bcode/ ~/.cache/bcode/before upgrading. No migration code.browser-harness-js@95b7a22ais the last "verbatim" event. No sync cadence. Python-harness behaviors of interest get watch-listed and ported individually.<projectDir>/.bcode/agent-workspace/is a flat dir of.tsfiles the agent owns and edits with the standardread/write/edittools. Snippet scope binds onlysession. Reuse =await import("/abs/path?t=" + Date.now()). Same mechanism for a 5-line wrapper and a 500-line scrape script.git clone && bcodeshares the agent's accumulated scripts (.bcode/agent-workspace/is tracked-by-default).browser_executeis unchanged in mental model (write a snippet, drive the browser, save reusable code as.tsfiles). Only the snippet language (Python → JS) and process model (subprocess → in-process) change.browser_open_cloudlands in the same PR asbrowser_execute.export namespacecarve-out forgenerated.tsdocumented inEXCEPTIONS.md.Verification
bun typecheckclean across all browsercode packages (@browser-use/browsercode-core,@browser-use/bcode-browser,@browser-use/bcode-laminar,@opencode-ai/{core,plugin,sdk}).bun testfrompackages/bcode-browser/: 9/9 pass (4 workspace-import always-on; 5 chrome-gated underBCODE_SMOKE_CHROME=1).bun run --cwd packages/opencode build -- --single): producesbcode-linux-x64, smoke-test--versionpasses.Release-notes draft (for the v0.1.0 tag)
Review notes
git rebase -ito break out the four commits already on the branch (9811ba170step 1+2,13d028a94step 3+4,8875a7c33step 5,d5ddc7880step 6).SessionStore.evictis wired but not called from outside. Cloud browsers stay running until the bcode process exits (which then closes the WS, which the BU side handles). This matches today'suv runsubprocess shape (a stuck Python interpreter also outlives the bcode session). Not a regression; documented incloud-browser.ts.