OpenCode developer explains its custom interpreter's QuickJS trade-offs
OpenCode's developer says its code mode uses a custom interpreter to avoid QuickJS's WebAssembly, worker-thread and serialization costs. A practitioner argues QuickJS suits server agents needing tighter privilege boundaries.

TL;DR
- OpenCode uses a custom code-mode interpreter to avoid QuickJS integration costs, according to thdxr's explanation.
- Large database results make cross-thread serialization a concern, as thdxr explained in a follow-up.
- QuickJS has an advocate for restricted server agents: 0xblacklight argued that their security model differs from local coding agents with a shell.
- Nested code mode creates an orchestration problem: jonas wants one code layer at the top of the harness stack.
The package README still calls @opencode-ai/codemode private to the workspace. Its design document describes stripping TypeScript syntax before parsing with Acorn. Meanwhile, Pi's implementation notes describe QuickJS inside Wasm with no network, filesystem or timers.
A bounded JavaScript interpreter
CodeMode gives the model one execute tool backed by an owned tree-walking interpreter, according to its design document. Execution happens without eval.
Hosts define schema-described tools and expose them as an object tree through the package thdxr linked. Within its bounded JavaScript subset, generated programs can:
- Sequence dependent tool calls, branch and loop.
- Transform, filter and aggregate intermediate results before returning them to the agent loop.
- Start independent calls concurrently and await them with
Promise.all. - Return JSON-safe data and structured diagnostics for program, validation, limit or tool failures.
GeoffreyHuntley described the approach as program synthesis in a reply, saying users are accustomed to defining individual tools and calling them.
QuickJS and large result sets
thdxr listed three costs behind the decision to avoid QuickJS:
- WebAssembly complexity.
- Worker-thread integration.
- Serialization of values moving between threads.
Code-mode scripts tend to compose tool calls, sometimes carrying large result sets from database queries. Sending those results back and forth between threads was the issue thdxr raised in a follow-up.
The database-result example is the sharpest argument for owning the runtime.
Building an interpreter would previously have taken too much effort, but was now easy enough to justify, thdxr said in another reply.
Server-agent privileges
QuickJS suits headless background agents that need restricted privileges and access, 0xblacklight argued. He distinguished that deployment from a coding agent already running locally with a bash shell.
OpenCode's custom interpreter also confines programs to supplied tools. Its design document assigns several boundaries explicitly:
- Authority: the host chooses available tools; each leaf tool enforces authorization and side-effect policy. Catalog visibility does not grant execution authorization.
- Ambient access: filesystem, process, environment, network and credential access must go through supplied tools. Modules, imports, arbitrary host globals and prototype mutation are unavailable.
- Validation: Effect Schemas validate and transform tool inputs and outputs. JSON Schemas only render model-facing signatures; their values still cross the plain-data boundary.
- Budgets:
timeoutMs,maxToolCallsandmaxOutputByteshave no package defaults. At most eight tool calls execute concurrently.
A 2,000-token tool catalog
Inline tool signatures have a default budget of 2,000 estimated tokens, calculated as characters divided by four, in the package README. Fixed instructions and namespace summaries sit outside that budget.
- Every namespace remains listed with its tool count.
- Complete, JSDoc-annotated signatures are selected round-robin across namespaces, preventing one large namespace from consuming the entire catalog.
tools.$codemode.searchis always registered, even when every signature fits. Instructions advertise it when the inline catalog is partial.- Search returns directly usable JavaScript paths, descriptions and complete TypeScript signatures, with deterministic ranking and pagination.
Nested code mode and operation enumeration
Two compatibility problems appear when code mode meets existing tool interfaces:
- Nested execution: Pi's Cloudflare MCP examples show a JavaScript program carrying another JavaScript program to an inner executor. Its implementation notes identify double JSON escaping and the inner program's inability to call outer tools as consequences.
- Operation enumeration: jonas said his API exposes a single
run(script)function and cannot enumerate every callable function ahead of time in the same thread. The ChatGPT plugin guidelines he quoted call for each model-callable operation to be exposed individually and prohibit a generic executor from enabling unlisted operations.