Skip to content

fix(browser): add btoa/atob/TextEncoder/TextDecoder to the run sandbox - #344

Merged
ankitranjan7 merged 5 commits into
mainfrom
fix/browser-run-globals
Aug 18, 2026
Merged

fix(browser): add btoa/atob/TextEncoder/TextDecoder to the run sandbox#344
ankitranjan7 merged 5 commits into
mainfrom
fix/browser-run-globals

Conversation

@ankitranjan7

@ankitranjan7 ankitranjan7 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Closes #335.

What

The browser run QuickJS sandbox shipped working base64 and UTF-8 codecs under private-looking names (__webcmdEncodeBase64, __webcmdDecodeBase64, __webcmdEncodeText, __webcmdDecodeText) and nothing under the standard ones. An internal agent-behaviour eval showed agents walking the standard-library ladder down — Buffer, then TextEncoder, then btoa — and finding every rung missing, then abandoning the real task.

This aliases the existing implementations to their platform names inside the same shim block in src/browser/run/runner.ts:

  • btoa / atob
  • TextEncoder / TextDecoder

Buffer deliberately stays absent (Node-only; #336 covers pointing agents at the replacement). No new dependency, no new file.

Latin1, not UTF-8

btoa/atob are binary-string codecs. The obvious composition __webcmdEncodeBase64(__webcmdEncodeText(v)) would silently UTF-8-expand any byte above 0x7F, so btoa(String.fromCharCode(255)) would return the base64 of two bytes instead of one. btoa here maps charCodeAt per character and throws an InvalidCharacterError above 255; atob is its inverse and returns a binary string. TextEncoder/TextDecoder do wrap the UTF-8 helpers, since those are UTF-8 by definition. TextDecoder.decode accepts Uint8Array, ArrayBuffer, or any view.

Tests

Five tests added to src/browser/run/runner.test.ts (the shim only exists inside a real runBrowserProgram run, so quickjs-host.test.ts cannot see it):

  1. all four globals present, Buffer still absent
  2. btoa('hello') === 'aGVsbG8=', atob(btoa(x)) round-trip, and a [0, 128, 255] binary round-trip matching Node's base64
  3. btoa above the latin1 range throws InvalidCharacterError
  4. TextEncoder/TextDecoder UTF-8 round-trip through café € 🎉, byte-for-byte against Node, including decoding from a raw ArrayBuffer
  5. typed-array serializer round-trip in both directions — a regression guard for the wire path; it passes on main too

Counts

npm run build clean.

npx vitest run --project unit:

passed skipped
main, no Chromium installed 2457 57
main, Chromium installed 2513 1
this branch, Chromium installed 2518 1

runner.test.ts is gated on a local Chromium being present, so the stated 2457 baseline is the no-Chromium figure. I installed Chromium via node_modules/playwright-core/cli.js install chromium so these tests actually execute; the +5 delta is exactly the five tests above. 0 failed throughout.

Docs

Left alone deliberately. skills/webcmd-browser/references/browser-run-playwright.md is being rewritten in #342; adding a row there now would just conflict. The "What is available" table in that rewrite should gain a text/binary row once both land — its existing "no Buffer" line stays accurate.

Cloud

No change needed. webcmd-cloud imports runBrowserProgram from @agentrhq/webcmd/browser/run at src/browser/hosted-browser.ts:17, so hosted runs execute this exact sandbox. The fix reaches hosted when the cloud bumps its pinned @agentrhq/webcmd (currently 0.7.2 on its main) — not bumped here. webcmd-cloud/src/browser/run-artifact-sink.ts implements BrowserRunArtifactSink, which this PR does not touch.

🤖 Generated with Claude Code

The QuickJS sandbox for `browser run` exposed working base64 and UTF-8
codecs only under private-looking `__webcmd*` names, so programs reaching
for the standard globals hit `'TextEncoder' is not defined`.

Alias them to the platform names in the existing platform shim. btoa/atob
are implemented as latin1 binary-string codecs rather than composed with
the UTF-8 helpers, which would corrupt binary input. Buffer stays absent —
it is Node-only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🟠 Maintainer review suggested — low confidence

The automated review could not reach a fully supported conclusion.

Limitations

  • The automated review returned an invalid structured result.
  • Some review context was unavailable or reduced.

This review is advisory and does not block merging.

ankitranjan7 and others added 4 commits August 18, 2026 23:01
…it (#345)

__webcmdWriteArtifact was the only write path out of the QuickJS sandbox
and its dunder prefix reads as private, so programs would not call it.
Expose the same function as globalThis.writeArtifact, keep the dunder as
an alias, and return the receipt instead of swallowing it.

Also accept a null contentType, which is what JSON.stringify produces for
the two-argument call, and document artifacts in the browser-run reference
including the download path and how a receipt is redeemed.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
# Conflicts:
#	skills/webcmd-browser/references/browser-run-playwright.md
#	src/browser/run/runner.test.ts
#	src/skills.test.ts
* benchmakrs added

* fix: removed missing refs from readme

* feat(benchmarks): support OpenAI as judge provider

* fix: screenshot upload

* feat: added cost support

* docs: design benchmark-specific judge prompts

* feat(benchmarks): split general and stealth judge prompts

* Delete docs/superpowers/specs directory

* chore: bump version metadata for 0.7.0 release

* Revert "chore: bump version metadata for 0.7.0 release"

This reverts commit c69011f.

* feat(benchmarks): expose full Webcmd skill pack to Codex evals

* feat(benchmarks): clarify stealth captcha handling

* fix: added support for webcmd browser skills only

* feat(benchmarks): run eval attempts in parallel

* fix(benchmarks): isolate parallel webcmd sessions

* test(benchmarks): cover parallel webcmd sessions

* pi support (#313)

* chore(benchmarks): pin webcmd 0.7.1

* refactor: disable concurrency in benchmark evaluation and remove integration tests

* feat(benchmarks): support Codex subscription evals

* docs(skills): improve browser diff and form guidance

* docs(skills): sync browser skill sources

---------

Co-authored-by: beubax <tejasr@bu.edu>
Co-authored-by: Tejas <48682479+beubax@users.noreply.github.com>
@ankitranjan7
ankitranjan7 merged commit 5d42052 into main Aug 18, 2026
15 checks passed
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.

browser run: QuickJS sandbox lacks btoa/atob/TextEncoder/TextDecoder while working equivalents exist as private __webcmd* globals

2 participants