Browser host for ACT — runs signed wasm agent tools in a browser tab.
The whole agent stack, no server:
| Where it lives | |
|---|---|
| LLM | wherever you want — remote API, or local via WebLLM |
| Tools | the browser, via this package |
| User data | the browser (IndexedDB, OPFS) |
ACT components are sandboxed wasm modules signed by their author and distributed via OCI registries. This package loads one in a browser tab using jco's in-browser transpiler — no server, no Node, no npm install required for the tool. The end user opens a page; the tool runs in their tab.
Experimental (0.1.0-alpha). The runtime works end-to-end for act:tools/tool-provider@0.2.0. Streaming results, OCI pull, and Sigstore verification are scheduled for follow-up versions.
Requires JSPI (JavaScript Promise Integration) for WebAssembly:
| Browser | JSPI status (2026-05) | Source |
|---|---|---|
| Chrome 137+ stable / Edge | shipped by default | Interop 2026 #10 |
| Firefox Nightly 152+ | 93% WPT pass rate | wpt.fyi |
| Safari Tech Preview 243+ | 93% WPT pass rate | wpt.fyi |
| Firefox stable / Safari stable | shipping during 2026 per Interop 2026 pledge | — |
All four major browsers committed to JSPI parity in Interop 2026.
npm install @actcore/host @bytecodealliance/jco @bytecodealliance/preview2-shimjco (which pulls in @bytecodealliance/jco-transpile) and preview2-shim are
runtime dependencies; bundle them with your app or load via importmap.
jco 1.24 browser note. jco 1.24's documented browser entry (
@bytecodealliance/jco/component) is broken in the published package — theobj/glue it imports is gitignored out of the tarball — and@bytecodealliance/jco-transpile's.export statically imports node: builtins, so it can't load under native ESM. This package therefore drives the one browser-safe artifact jco ships: the vendored, componentized bindgen at@bytecodealliance/jco-transpile/vendor/js-component-bindgen-component.js(the samegenerate()jco's browser entry wraps). That subpath is not in jco-transpile'sexportsmap, so map it explicitly in your importmap (seeexamples/basic.html) or add a bundler resolve alias.
import { runComponent } from '@actcore/host';
const wasm = new Uint8Array(await (await fetch('/time.wasm')).arrayBuffer());
const { toolProvider } = await runComponent(wasm, {
// Where the @bytecodealliance/preview2-shim browser files live. Use a CDN,
// your bundler's resolved path, or your dev-server alias. preview2-shim 0.19
// serves its browser build from dist/browser/ (it was lib/browser/ before).
shimBase: 'https://esm.sh/@bytecodealliance/preview2-shim@0.19.0/dist/browser/',
});
const { tools } = await toolProvider.listTools([]);
console.log(tools); // [{ name: 'get_current_time', description: ..., parametersSchema: ... }]
const result = await toolProvider.callTool(
'get_current_time',
new Uint8Array([0xa0]), // CBOR {} — empty args
[],
);
if (result.tag === 'immediate') {
for (const ev of result.val) {
if (ev.tag === 'content') {
console.log(new TextDecoder().decode(ev.val.data)); // → "2026-05-11T15:13:23.464+00:00"
}
}
}See examples/basic.html for a runnable demo. From a fresh
clone, npm run sync-wit once to fetch the WIT deps (via wkg; see wit/README.md), then npm run build. Run the demo with npm run example (serves the package root so the importmap's /node_modules/… paths resolve) and open http://localhost:8765/examples/basic.html.
runComponent(bytes, options):
- Calls jco's low-level bindgen
generate()(~250ms for a 1MB component) withasyncMode: jspiand an explicitmappointing WASI specifiers atpreview2-shimbrowser builds (andwasi:httpp3 at the bundled shim). Drivinggenerate()directly — rather thantranspileBytes— keeps the transpiler node-free and lets us own the WASI map, sidestepping jco 1.24's default map that routes p3 WASI onto the Node-onlypreview3-shim. - Applies a thin patch to the emitted JS:
- rewrite bare
preview2-shimspecifiers to absolute URLs (blob: contexts can't see the page's importmap), - short-circuit future/stream drops whose wasm-side end was already transferred (a wit-bindgen Rust quirk on the wasi:http path).
- rewrite bare
- Materialises the patched JS +
.core.wasmas blob URLs, dynamic-imports the entry module, and returns the exportedtoolProvider.
The wasip3-async lift bugs that needed heavy patching under jco 1.19 (STREAM_TABLES/FUTURE_TABLES declarations, HostFuture, host-resource lowering, _liftFlatRecord task-return, storageLen accounting) are all fixed upstream as of jco 1.24 (bindgen 2.0.3) — jco lifts list-tools / call-tool results natively. This package is now a thin glue layer.
- v0.2 — OCI pull and Sigstore (cosign) signature verification, via a shared
act-oci-verifyRust crate compiled to both native (foract-cli) andwasm32-wasip2(loaded here, hash-pinned in source). - v0.2 — Streaming tool results (
ToolResult::streaming). - v0.x —
act:sessions/session-providersupport. - v0.x — Web Worker isolation (run components off the main thread).
MIT OR Apache-2.0