Runtime status
Scope
Code mode owns the model-facing orchestration shape for a prepared run. It does not own model selection, channel behavior, auth, tool policy, or tool implementations. In scope: model-visible control/direct tool definitions, hidden tool catalog construction, JavaScript guest execution, the QuickJS-WASI worker runtime, host callbacks for search/describe/call, resumable state for suspended guest programs, output/timeout/memory/pending-call/snapshot limits, and telemetry/trajectory projection for nested tool calls. Out of scope: provider-native remote code execution, shell execution semantics, changing existing tool authorization, persistent user-authored scripts, package manager/file/network/module access in guest code, and direct reuse of Codex Code Mode internals. Provider-owned tools such as remote Python sandboxes are separate tools. See Code execution.Terms
- Code mode: the OpenClaw runtime mode that hides catalog-compatible model
tools and exposes
exec,wait, plus required direct-only tools. - Guest runtime: the QuickJS-WASI JavaScript VM that evaluates model code.
- Host bridge: the narrow JSON-compatible callback surface from guest code back into OpenClaw.
- Catalog: the run-scoped list of effective tools after normal tool policy, plugin, MCP, and client-tool resolution.
- Nested tool call: a tool call made from guest code through the host bridge.
- Snapshot: serialized QuickJS-WASI VM state saved so
waitcan continue a suspended code-mode run.
Nested tool execution
Every nested tool call crosses the host bridge and re-enters OpenClaw, preserving: active agent id, session id and key, sender and channel context, sandbox policy, approval policy, pluginbefore_tool_call hooks, abort
signal, streaming updates where available, and trajectory/audit events.
Completed nested calls persist as bounded, redacted display-only activity, retaining
their original parent and invocation ids across history reloads. Provider replay
contains only the actual model calls; child activity adds no synthetic model turns.
Starts and partial updates remain transient. Older missing child history cannot be
reconstructed from source code or outer results.
Nested tool failures cross into the guest as catchable JavaScript errors. If
guest code does not catch an error, exec or wait returns a failed tool
result and the agent can continue normally. Follow the
tool-error guidance to inspect possible partial
effects before choosing another action. Network-controlled tool output and errors
retain their existing untrusted-content wrapping and sanitization; continuing
after a failure does not grant new permissions or replay completed side effects.
Nested calls honor each tool’s executionMode. A "sequential" tool waits for
earlier catalog calls to finish and blocks later calls until its result has been
accepted. Parallel-capable calls can overlap before the next sequential call.
Scheduling is shared across cells using the same run catalog; separate catalogs
remain independent. Queued calls are canceled when their caller or catalog closes.
maxPendingToolCalls caps in-flight bridge requests, not the size of an ordinary
Promise.all batch. Calls and timers beyond that cap wait in the guest alongside
Swarm requests. At most 128 ordinary requests can be queued,
independently of the configured in-flight cap, using the existing accepted
bridge-limit ceiling. Swarm launches, notes, and result waits do not consume this
ordinary quota; their existing group, VM memory, and snapshot limits still apply.
Queued inputs and request identities survive snapshot/resume; clearTimeout
removes a queued timer without starting a host timer. A queued timer’s delay begins
when it gets a bridge slot. Guest continuations run before waiting requests refill
available slots, and fast requests still drain within the same exec or wait.
Creating more ordinary requests than their queue quota allows fails the worker leg with
invalid_input and guidance to await smaller batches. Catching the immediate
JavaScript error does not admit a partial batch: no new calls from that
synchronous frontier are dispatched. Earlier worker legs may already have run
tools; inspect their effects rather than replaying the cell. Queueing does not
raise memory, snapshot, time, or headless total tool-call limits, or bypass
cancellation and policy checks.
Run and snapshot lifecycle
Each code-mode run is tracked in an in-process map keyed byrunId (not
persisted to disk or a database). exec/wait return one of three result
statuses: completed, waiting, or failed.
- A
waitingresult stores the QuickJS snapshot, pending bridge requests, and scoping metadata (agent run id, session id/key) untilwaitresumes it or it expires. - Expiry, wrong-session, wrong-run, and unknown/already-resuming
runIdvalues do not produce a distinct terminal status; they surface as afailedresult (code: "invalid_input") with a message such ascode mode run is unavailable or expired.orcode mode run belongs to a different session.. - A run’s snapshot is removed from the map as soon as it settles to
completedorfailed, or is dropped on Gateway shutdown (nothing survives a restart: this is transient runtime state). - OpenClaw caps the number of concurrently suspended runs per process (64) and
rejects new suspensions past that cap with
too many suspended code mode runs..
maxSnapshotBytes per run, the per-process
suspended-run cap above, and snapshotTtlSeconds. The worker checks the snapshot
size, including QuickJS metadata, before handing pending work to the Gateway.
These limits and memoryLimitBytes bound guest state, not total Gateway memory;
warm worker threads also retain memory.
Explicit results.save(value) references keep normalized JSON in the existing
admitted catalog lifetime, independently of each cell’s VM and output budget.
The store admits at most 64 entries and min(memoryLimitBytes, maxSnapshotBytes)
encoded JSON bytes (10 MiB by default), in addition to the cell’s program-data
inbox. This is a logical data allowance, not a process RSS limit. Capacity errors
preserve existing entries; deletion frees their capacity. Loads return detached
copies and carry forward network-content provenance into the receiving cell’s
normal untrusted output wrapper.
Interactive cells also retain final structured JSON automatically when byte or
model-result fitting would otherwise truncate it. The worker serializes the
final value once and retains at most the larger of the display allowance and
the existing memory/snapshot data allowance; only eligible interactive cells
request this capture. Catalog admission checks remaining bytes and entries
before parsing another full JSON copy for bounded preview construction. The
normalized string moves into the same store. Final projection reserves a usable
reference before allocating the remaining display space to sampled descriptions
and output; an undisplayable reference is released. Failed admission remains a
successful partial result with a precise non-retention reason. Headless and
restart-safe execution do not allocate automatic references.
Catalog teardown, replacement, restriction, and the admitted run’s abort clear
saved data. Appended client tools preserve the same result-store lifetime, including
for cells already parked in wait. Each cell captures that store before execution,
so stale cells cannot adopt a replacement store or retain references after a
permission change.
Saved references are data snapshots and never execution authority. They do not
survive Gateway restart and cannot be used by another run or session.
QuickJS-WASI runtime
OpenClaw loadsquickjs-wasi as a direct dependency in the owning package; it
does not rely on a transitive copy installed for an unrelated dependency.
Runtime responsibilities: compile/load the QuickJS-WASI WebAssembly module;
create one isolated VM per code-mode run or resume; register host callbacks
by stable names; set memory and interrupt limits; evaluate JavaScript; drain
pending jobs; snapshot suspended VM state; restore snapshots for wait;
dispose VM handles and snapshots after terminal states.
Snapshot buffers transfer directly between workers and the Gateway without
copying the VM heap through a storage serialization format.
The runtime executes in a Node.js worker thread, outside OpenClaw’s main
event loop. A guest infinite loop must not block the Gateway process
indefinitely; the worker’s interrupt handler enforces the wall-clock timeout
independent of guest code cooperating.
TypeScript
TypeScript-style signatures describe tool inputs and outputs to the model through the quick index, catalog handles, andAPI.read declaration files. Unknown
outputs stay unknown, and declarations do not grant access to additional tools.
Executable cells are plain JavaScript. Code Mode does not load a TypeScript
compiler, strip annotations, or typecheck the program. QuickJS parses and runs
the JavaScript directly. Tool calls still use the existing runtime input and
output validation, policy, and approval owners. A later call can fail after
earlier calls have produced effects, so follow the
recovery guidance before
retrying a failed cell.
Security boundary
Model code is hostile. The runtime uses defense in depth:- runs QuickJS-WASI outside the main event loop, in a worker thread
- loads
quickjs-wasias a direct dependency, not through Codex or a transitive package - no filesystem, network, subprocess, module import, environment variables, or host global objects in the guest
- uses QuickJS memory and interrupt limits plus a parent-process wall-clock timeout
- enforces output, snapshot, log, and pending-call caps
- serializes host bridge values through a narrow JSON adapter
- converts host errors into plain guest errors, never host realm objects
- drops snapshots on timeout, abort, session end, or expiry
- rejects recursive access to
exec,wait, and Tool Search control tools - reserves specialized globals and resolves callable-name collisions before the worker starts