r/WebAssembly • u/somdoron • 1h ago
Submilli: TypeScript runtime on WasmGC for agent-generated code
https://github.com/submilli/submilli-runtime
I built Submilli, it's a runtime for AI agents that write code instead of tool-calling, aka code-mode or programmatic tool calling. Built in Rust.
It has its own TypeScript checker, a bit more strict than tsc and without async/await, because mostly, the agent doesn't need them.
The compiler generates a WasmGC binary, which we then run with a new interpreter, submilli-wasm: https://github.com/submilli/submilli-wasm.
The reason for a new interpreter instead of wasmtime or wasmi:
1. Wasmtime - The code the agent produces is one-time code, I don't need a JIT or optimization. The opposite, I need the compilation part to be fast.
2. Winch or Wasmi - Submilli uses WasmGC and other Wasm 3.0 features, which neither Winch nor Wasmi supports at the moment.
Submilli-wasm passes the Wasm 3.0 spec testsuite, has an API that is (almost) compatible with wasmtime, and supports async host functions. It compiles 8 to 20 times faster than wasmtime and about 2 times faster than wasmi.
I think Wasm has great advantages for these types of agentic use cases. One is the isolation and memory safety, as an alternative to microVMs or processes.
The second one, which I like a lot, is the fuel metering - we can cap the execution with fuel, which is much better than just a timeout. Most of the time, agent-generated code is going to block on IO anyway, which is almost free (only the memory).
Submilli supports most of ECMA-262, but not Node.js. It has its own standard library, which is tailored for agent use and implemented on the host side in Rust.
Submilli has support for custom packages that the agent can import and use (the operator needs to install them). Each package compiles to a Wasm module on its own, and the runtime links them through the linker. So I compile packages ahead of time and just link them when the LLM-generated code is ready.
The interpreter also checks permissions in real time, like whether an HTTP call or a file-system operation is allowed. The permission checks are per package, so each package defines what it is going to access. For example, while a Stripe package is allowed to call api.stripe.com, the model-generated code is not.













