zidane/ codemode
A QuickJS sandbox that runs model-written JavaScript with only injected tools as its capabilities.
import { … } from 'zidane/codemode'src/codemode/index.ts
Use CodemodeSandbox to let a model write one script that calls many tools, so the intermediate results never enter its context. The script runs in a QuickJS VM in a worker thread, with no timers, network or modules, and needs the optional quickjs-wasi package. The render functions turn your tools into TypeScript declarations to show the model.
import { CodemodeSandbox } from 'zidane/codemode'
interface AddArgs { a: number, b: number }
const sandbox = new CodemodeSandbox({
tools: [{ name: 'add', execute: (args) => { const { a, b } = args as AddArgs; return a + b } }],
})
const result = await sandbox.execute('return await tools.add({ a: 1, b: 2 })')
if (result.ok)
console.log(result.value)
await sandbox.close()Functions#
codemodeSandboxPathsFunction#
The paths set with setCodemodeSandboxPaths.
function codemodeSandboxPaths(): Readonly<CodemodeSandboxPaths>Returns
Readonly<CodemodeSandboxPaths>loadQuickJSWasmFunction#
Read and compile the QuickJS wasm once per path. path defaults to the file in the installed
quickjs-wasi package (an optional peer dependency); pass it when the file lives elsewhere, for
example embedded in a Bun compiled executable. A failed load is retried on the next call.
function loadQuickJSWasm(path?: string): Promise<object>Parameters
| Name | Type | Description |
|---|---|---|
| path? | string | Path to |
Returns
Promise<object>mcpStructuredContentSchemaFunction#
The structuredContent schema of an MCP CallToolResult output schema (detected by a
content array of objects, boolean isError, and object _meta), true when it declares none, or
undefined when the schema is not a CallToolResult.
function mcpStructuredContentSchema(
schema: CodemodeJsonSchema | undefined,
): CodemodeJsonSchema | undefinedParameters
| Name | Type | Description |
|---|---|---|
| schema | CodemodeJsonSchema | undefined | Output schema of a tool. |
Returns
CodemodeJsonSchema | undefinedparseCodemodeSourceFunction#
Split an optional first-line // @options: {...} from the script. Throws
CodemodeSourceError for empty input and invalid options.
function parseCodemodeSource(input: string): ParsedCodemodeSourceParameters
| Name | Type | Description |
|---|---|---|
| input | string | Script text, optionally starting with an |
Returns
Example
import { parseCodemodeSource } from 'zidane/codemode'
const { code, options } = parseCodemodeSource(
'// @options: {"timeout_ms": 5000}\ntext("hi")',
)
console.log(options.timeoutMs, code)renderDeclarationsFunction#
Render TypeScript declarations for the script-visible API. Tools become members of
declare const tools, globals become declare function statements, and ns.member globals
members of declare const ns. Descriptions become doc comments; schemas become types.
function renderDeclarations(options: RenderDeclarationsOptions): stringParameters
| Name | Type | Description |
|---|---|---|
| options | RenderDeclarationsOptions | Tools and globals to render. |
Returns
stringExample
import { renderDeclarations } from 'zidane/codemode'
const declarations = renderDeclarations({
tools: [{
name: 'read_file',
description: 'Reads a file.',
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
execute: () => '',
}],
})
console.log(declarations)renderToolOutputTypeFunction#
The type a tool call resolves to: CallToolResult<T> for MCP output schemas (needs
MCP_TYPESCRIPT_PREAMBLE), the schema's type otherwise, and unknown without a schema.
function renderToolOutputType(
schema: CodemodeJsonSchema | undefined,
options?: { comments?: boolean },
): stringParameters
| Name | Type | Default | Description |
|---|---|---|---|
| schema | CodemodeJsonSchema | undefined | Output schema of a tool. | |
| options? | { comments?: boolean } | {} | Rendering options. |
Returns
stringrenderToolSampleFunction#
A tool's sample: the description followed by the tool's declaration. Used for tool
listings and ALL_TOOLS entries.
function renderToolSample(
tool: Pick<CodemodeTool, 'name' | 'description' | 'inputSchema' | 'outputSchema'>,
options?: { inputMaxChars?: number },
): stringParameters
| Name | Type | Default | Description |
|---|---|---|---|
| tool | Pick<CodemodeTool, 'name' | 'description' | 'inputSchema' | 'outputSchema'> | Tool whose description and declaration to render. | |
| options? | { inputMaxChars?: number } | {} |
|
Returns
stringrenderToolSignatureFunction#
One tool as a member of the tools object: name(args: T): Promise<R>; with the
name as the identifier scripts use. Input types longer than inputMaxChars render as unknown.
A tool that takes a bare string (stringArgumentOf) renders as name(args: string | T).
Tools whose output schema is an MCP CallToolResult render as Promise<CallToolResult<T>>,
which needs MCP_TYPESCRIPT_PREAMBLE.
function renderToolSignature(
tool: Pick<CodemodeTool, 'name' | 'inputSchema' | 'outputSchema'>,
options?: { comments?: boolean; inputMaxChars?: number },
): stringParameters
| Name | Type | Default | Description |
|---|---|---|---|
| tool | Pick<CodemodeTool, 'name' | 'inputSchema' | 'outputSchema'> | Tool whose name and schemas to render. | |
| options? | { comments?: boolean; inputMaxChars?: number } | {} | Rendering options. |
Returns
stringschemaToTypeFunction#
Convert a JSON Schema to a TypeScript type expression: objects on one line ({ a: string; b?: number; }) with properties sorted by name,
or one property per line with // comments when a property has a description; Array<T> for
arrays. Local references (#/$defs/..., #/definitions/...) resolve against schema;
recursive and remote references render as unknown. A result longer than maxChars renders as
unknown.
function schemaToType(
schema: CodemodeJsonSchema,
options?: { comments?: boolean; maxChars?: number },
): stringParameters
| Name | Type | Default | Description |
|---|---|---|---|
| schema | CodemodeJsonSchema | JSON Schema to convert. | |
| options? | { comments?: boolean; maxChars?: number } | {} | Conversion options. |
Returns
stringsetCodemodeSandboxPathsFunction#
Replace the process-wide sandbox paths. Explicit per-sandbox options
(CodemodeSandboxOptions.workerUrl, loadQuickJSWasm(path)) still win.
function setCodemodeSandboxPaths(paths: CodemodeSandboxPaths): voidParameters
| Name | Type | Description |
|---|---|---|
| paths | CodemodeSandboxPaths | Paths to use; omitted fields fall back to the defaults. |
stringArgumentOfFunction#
The property a bare string argument fills: the schema's only required property, when it is a
string. A script may then call tools.shell("ls") for tools.shell({ command: "ls" }), or
tools.shell("ls", { timeout: 5000 }) to pass the other properties too. undefined otherwise.
function stringArgumentOf(schema: CodemodeJsonSchema | undefined): string | undefinedParameters
| Name | Type | Description |
|---|---|---|
| schema | CodemodeJsonSchema | undefined | Input schema of a tool. |
Returns
string | undefinedtoCodemodeIdentifierFunction#
The identifier a script uses for a tool: characters that are not valid in a JavaScript
identifier become _. mcp__docs__search stays as is, my-tool becomes my_tool.
function toCodemodeIdentifier(name: string): stringParameters
| Name | Type | Description |
|---|---|---|
| name | string | Tool name to convert. |
Returns
stringClasses#
CodemodeSandboxClass#
Runs JavaScript in a QuickJS VM (a separate wasm instance) inside a worker
thread. The script sees tools.<name>(args) for every registered tool, ALL_TOOLS,
the output helpers text, image, exit, and console.*, store/load, and the
configured globals; nothing else (no timers, fetch, process, require, modules).
Each execute() gets its own worker and VM; the sandbox only holds the tool
table and defaults. close() aborts in-flight executions.
class CodemodeSandboxConstructor
new CodemodeSandbox(options?: CodemodeSandboxOptions)Creates a sandbox with its tools, globals and defaults. Throws when a global or shortcut name is invalid, reserved or duplicated.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| options? | CodemodeSandboxOptions | {} | Tools, globals, timeout, memory limit and worker settings. |
Returns
Properties
| Name | Type | Description |
|---|---|---|
| globalsreadonly | CodemodeTool[] | Globals the script can call as top-level functions. |
| toolsreadonly | CodemodeTool[] | Tools currently registered. |
Methods
close()
close(): Promise<void>Aborts in-flight executions (they resolve with kind: "aborted") and rejects new ones.
Returns
Promise<void>execute()
execute(code: string, options?: CodemodeExecuteOptions): Promise<CodemodeResult>code is an async function body: return and top-level await work.
Script failures come back as { ok: false }. The promise rejects only when the sandbox is closed.
The script can use store(key, value) and load(key) on options.store.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| code | string | Script source, run as an async function body. | |
| options? | CodemodeExecuteOptions | {} | Per-execution signal, timeout, |
Returns
Promise<CodemodeResult>registerTool()
registerTool(tool: CodemodeTool): voidThrows if a tool with the same name is already registered.
Parameters
| Name | Type | Description |
|---|---|---|
| tool | CodemodeTool | Tool to add to the |
unregisterTool()
unregisterTool(name: string): booleanRemoves a tool by name. Returns true when it was registered.
Parameters
| Name | Type | Description |
|---|---|---|
| name | string | Name of the tool to remove. |
Returns
booleanExample
import { CodemodeSandbox } from 'zidane/codemode'
const sandbox = new CodemodeSandbox({
tools: [{ name: 'add', execute: args => (args as { a: number, b: number }).a + (args as { a: number, b: number }).b }],
})
const result = await sandbox.execute('return await tools.add({ a: 1, b: 2 })')
if (result.ok)
console.log(result.value)CodemodeSourceErrorClass#
Error thrown when codemode source is empty or its @options line is invalid.
class CodemodeSourceError extends ErrorConstructor
new CodemodeSourceError(message: string)Creates the error.
Parameters
| Name | Type | Description |
|---|---|---|
| message | string | Description of the problem. |
Returns
CodemodeValueClass#
A tool result the script reads as value and prints as text. Printing it (return r,
text(r), console.log(r), or an array holding such results) shows text, the result as a
direct call returns it, while r.field reads value as usual. Return one from
CodemodeTool.execute when value is structured.
class CodemodeValueConstructor
new CodemodeValue(value: unknown, text: string)Parameters
| Name | Type | Description |
|---|---|---|
| value | unknown | What the script receives. Must survive a JSON round trip. |
| text | string | What printing the value shows. |
Returns
Properties
| Name | Type | Default | Description |
|---|---|---|---|
| [CODEMODE_VALUE]readonly | true | true | Brands the instance, so |
| textreadonly | string | What printing the value shows. | |
| valuereadonly | unknown | What the script receives. Must survive a JSON round trip. |
Methods
CodemodeValue.is()
CodemodeValue.is(candidate: unknown): candidate is CodemodeValueWhether candidate is a CodemodeValue, from any copy of this module.
Parameters
| Name | Type | Description |
|---|---|---|
| candidate | unknown | Value to check. |
Returns
candidate is CodemodeValueExample
const run: CodemodeTool = {
name: 'run',
execute: () => new CodemodeValue({ stdout: 'ok\n', exit_code: 0 }, 'ok\n(exit 0)'),
}Constants#
CODEMODE_OPTIONS_PREFIXConstant#
Codemode source format: JavaScript, optionally preceded by one options line.
// @options: {"max_output_tokens": 2000, "timeout_ms": 30000}
const text = await tools.read_file({ path: "package.json" });
text(JSON.parse(text).name);
const CODEMODE_OPTIONS_PREFIX = '// @options:'CODEMODE_SOURCE_GRAMMARConstant#
Lark grammar for providers with grammar-constrained tool input. It only fixes the shape of the
options line; the options JSON and the code are checked by parseCodemodeSource.
const CODEMODE_SOURCE_GRAMMAR: stringDEFAULT_INPUT_SCHEMA_MAX_CHARSConstant#
Largest rendered input type, in characters, before it becomes unknown.
const DEFAULT_INPUT_SCHEMA_MAX_CHARS = 16_000MAX_OUTPUT_CHARSConstant#
Output one script may produce with text(), image(), and console.*: characters of text and
base64 image data, and items. The host keeps all output until the script ends, so without a
limit a script that prints in a loop grows the host's memory until it crashes. The item limit
covers loops that print empty strings.
const MAX_OUTPUT_CHARS: numberMAX_OUTPUT_ITEMSConstant#
Maximum number of output items one script may produce.
const MAX_OUTPUT_ITEMS = 100_000MAX_STORE_TOTAL_CHARSConstant#
Maximum total size, in characters of JSON, of all values a script writes with store() in one execution.
const MAX_STORE_TOTAL_CHARS: numberMAX_STORE_VALUE_CHARSConstant#
Maximum size, in characters of JSON, of one value a script writes with store().
const MAX_STORE_VALUE_CHARS: numberMCP_TYPESCRIPT_PREAMBLEConstant#
TypeScript types for MCP results, from the MCP CallToolResult schema, so CallToolResult<T>
declarations can refer to them.
const MCP_TYPESCRIPT_PREAMBLE = ...Interfaces#
CodemodeCallInterface#
Record of one tool call made by a script.
interface CodemodeCallProperties
| Name | Type | Description |
|---|---|---|
| durationMs | number | Time the call took, in milliseconds. |
| name | string | Name of the tool that was called. |
| seq | number | Same as |
| status | CodemodeCallStatus | How the call ended. |
CodemodeDeterminismInterface#
Pins the VM's only nondeterministic inputs, the WASI clock and random
source. Date.now() returns nowMs for the whole run and Math.random()
(seeded from the clock by QuickJS) plus WASI random_get derive from
seed. With these set, a script that sees the same tool results takes the
same path, which is what durable replay needs.
interface CodemodeDeterminismProperties
| Name | Type | Description |
|---|---|---|
| nowMs | number | Fixed value returned by |
| seed | number | Seed for |
CodemodeErrorInterface#
Failure of a codemode execution.
interface CodemodeErrorProperties
| Name | Type | Description |
|---|---|---|
| excerpt? | string | Script errors with a location: the offending source line and a |
| kind | CodemodeErrorKind | What kind of failure occurred. |
| message | string | Error message. |
| name? | string | Error name from the script, for script errors. |
| stack? | string | Stack trace from the script, for script errors. |
CodemodeExecuteOptionsInterface#
Options for one CodemodeSandbox.execute call.
interface CodemodeExecuteOptionsProperties
| Name | Type | Description |
|---|---|---|
| determinism? | CodemodeDeterminism | Pin the clock and random source for this execution. See |
| onOutput? | (item: CodemodeOutputItem) => void | Called with each output item as the script produces it, before the execution ends. The result still carries every item. A throwing callback is ignored. |
| signal? | AbortSignal | Aborts the execution when signaled. The result is an |
| store? | Readonly<Record<string, unknown>> | Values the script reads with |
| timeoutMs? | number | Overrides the sandbox default for this execution. |
| trackLines? | boolean | Report the script line of each call as |
CodemodeSandboxOptionsInterface#
Options for creating a CodemodeSandbox.
interface CodemodeSandboxOptionsProperties
| Name | Type | Description |
|---|---|---|
| globals? | CodemodeTool[] | Functions exposed as top-level identifiers instead of on |
| memoryLimitBytes? | number | Maximum memory the QuickJS VM may allocate. Allocations beyond it fail inside the script as
|
| shortcuts? | Record<string, string> | Global functions that call a tool, by function name: |
| timeoutMs? | number | Overall deadline per execution, including time spent in tools. |
| toolHints? | Record<string, string> | Advice shown when a script reads a |
| tools? | CodemodeTool[] | Tools the script calls as |
| wasm? | object | Promise<object> | Compiled |
| workerUrl? | string | URL | Worker entry ( |
CodemodeSandboxPathsInterface#
Paths the sandbox uses instead of its defaults.
interface CodemodeSandboxPathsProperties
| Name | Type | Description |
|---|---|---|
| wasmPath? | string |
|
| workerUrl? | URL | The sandbox worker module. Default: |
CodemodeSourceOptionsInterface#
Options read from the // @options: line of a codemode script.
interface CodemodeSourceOptionsProperties
| Name | Type | Description |
|---|---|---|
| maxOutputTokens? | number | Token budget for the script's output. |
| timeoutMs? | number | Hard deadline for the whole script in milliseconds, including tool calls. |
CodemodeStoreWritesInterface#
Keys the script changed with store(). Only successful executions report writes.
interface CodemodeStoreWritesProperties
| Name | Type | Description |
|---|---|---|
| delete | string[] | Keys stored as |
| set | Record<string, unknown> | Keys written with a value, mapped to the stored value. |
CodemodeToolInterface#
A host function a codemode script can call, either as tools.<id>(args) or, for globals, as a top-level function.
interface CodemodeToolProperties
| Name | Type | Description |
|---|---|---|
| description? | string | Shown as a doc comment in |
| execute | (args: unknown, context: CodemodeToolContext) => unknown |
|
| inputSchema? | CodemodeJsonSchema | Schema of the single argument. Rendered as the parameter type; |
| name | string | The script calls tools as |
| outputSchema? | CodemodeJsonSchema | Schema of the resolved value. Rendered as the promise type; |
| signature? | string | Globals only: TypeScript parameter list and return type for |
| spread? | boolean | Globals only: |
CodemodeToolContextInterface#
Context passed to a CodemodeTool on each call.
interface CodemodeToolContextProperties
| Name | Type | Description |
|---|---|---|
| line? | number | Script line that made this call (the innermost frame of the script, so a
call inside a helper reports the helper's line). Only with
|
| seq | number | 1-based position of this call in the order the script issued it, shared by tools and globals. The VM is single-threaded and has no timers, so for a given script and given results the order is the same on every run: durable adapters can key journal entries on it. |
| signal | AbortSignal | Aborted when the script finishes (including unawaited calls), the execution times out, the caller aborts, or the sandbox is closed. |
ParsedCodemodeSourceInterface#
Result of parseCodemodeSource.
interface ParsedCodemodeSourceProperties
| Name | Type | Description |
|---|---|---|
| code | string | The script with the options line replaced by an empty line, so line numbers are unchanged. |
| options | CodemodeSourceOptions | Options from the options line. Empty when the script has none. |
RenderDeclarationsOptionsInterface#
Options for renderDeclarations.
interface RenderDeclarationsOptionsProperties
| Name | Type | Description |
|---|---|---|
| globals? | readonly CodemodeTool[] | Globals rendered as top-level |
| tools? | readonly CodemodeTool[] | Tools rendered as members of |
Types#
CodemodeCallStatusType#
Outcome of a tool call: finished (ok), threw (error) or was aborted before it settled (cancelled).
type CodemodeCallStatus = 'ok' | 'error' | 'cancelled'CodemodeErrorKindType#
Category of a CodemodeError.
type CodemodeErrorKind = 'script' | 'timeout' | 'aborted' | 'sandbox'CodemodeJsonSchemaType#
A JSON Schema document. Only used to render declarations; values are not validated against it.
type CodemodeJsonSchema = { [key: string]: unknown } | booleanCodemodeOutputItemType#
One item of the script's output, in the order the script produced it: text() and console.*
produce text items, with console: true for console.*, and image() image items. data is base64.
type CodemodeOutputItem =
| { console?: true; text: string; type: 'text' }
| { data: string; mimeType: string; type: 'image' }CodemodeResultType#
output is kept for failed executions too, up to the failure. exit() completes with value: undefined.
type CodemodeResult =
| { calls: CodemodeCall[]; ok: true; output: CodemodeOutputItem[]; storeWrites: CodemodeStoreWrites; value: unknown }
| { calls: CodemodeCall[]; error: CodemodeError; ok: false; output: CodemodeOutputItem[] }CodemodeWasmModuleType#
A compiled WebAssembly.Module of quickjs-wasi/quickjs.wasm. Typed opaquely so callers don't
need the DOM lib for the WebAssembly globals.
type CodemodeWasmModule = object