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.1.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 and preview2-shim are runtime dependencies; bundle them with your app or load via importmap.
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.
shimBase: 'https://esm.sh/@bytecodealliance/preview2-shim@0.17.0/lib/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. Build once with npm run build, then npm run example and open http://localhost:8765/basic.html.
runComponent(bytes, options):
- Calls
@bytecodealliance/jco/component's in-browsertranspile()(~250ms for a 1MB component) withasyncMode: jspiand an explicitmappointing WASI specifiers atpreview2-shimbrowser builds. - Patches the resulting JS to:
- declare missing
STREAM_TABLES/FUTURE_TABLES(jco 1.19 references them without declaring), - rewrite remaining bare specifiers to absolute URLs (blob: contexts can't see the page's importmap),
- bypass
_liftFlatRecordforlist-toolsandcall-tooltask-return, walking the wasip3-async flat-fields representation directly out of linear memory.
- declare missing
- Materialises the patched JS +
.core.wasmas blob URLs, dynamic-imports the entry module, and wraps the exportedtoolProviderso callers don't have to think about the lift dispatch.
The patches are stop-gaps for upstream jco issues; once they're fixed there, this package shrinks to 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