# Code Mode

Let the model run JavaScript cells that call registered tools, run calls in parallel, keep values between cells, and return only the output it needs.

## Overview

Code Mode gives the model an `exec` tool that runs a JavaScript cell. Inside a cell, each tool available in Code Mode is an async function on `tools`, so one cell can make many tool calls and return a short result.

Only the values that a cell passes to `text()` or `image()` return to the model. A cell can read ten files and return three lines.

A Rust runtime runs cells in an embedded evaluator by default. A TypeScript session runs them in the host's own JavaScript runtime with `code_mode: true`, on a remote executor, or on hands.

```mermaid
sequenceDiagram
  participant Model
  participant Cell as Code Mode cell
  participant Tools
  Model->>Cell: exec(JavaScript)
  par
    Cell->>Tools: tools.quotient({a:42,b:2})
  and
    Cell->>Tools: tools.quotient({a:1,b:0})
  end
  Tools-->>Cell: 21 and a rejection
  Cell-->>Model: text({half, zero})
```

## Walkthrough

The steps below run two cells against two Rust tools without a model or network access, through the same `ToolRuntime` that agents use. This walkthrough is Rust only, because nanocode-wasm exposes no `ToolRuntime`.

### Register the Tools

Code Mode calls the same tools that the runtime registers. These two tools report overflow and division by zero as errors.

```rust title="src/main.rs"
use nanocode::Tools;
use nanocode::code_mode::runtime::ToolRuntime;
use nanocode::tool_contract::ToolContext;

type Result<T> =
    std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

// Each tool becomes an async function on the cell's tools object
#[nanocode::tool(
    description = "Add two signed integers, reporting overflow"
)]
async fn checked_sum(a: i64, b: i64) -> std::io::Result<i64> {
    a.checked_add(b).ok_or_else(|| {
        std::io::Error::other("integer addition overflow")
    })
}

#[nanocode::tool(
    description = "Divide signed integers, reporting zero divisors"
)]
async fn quotient(a: i64, b: i64) -> std::io::Result<i64> {
    a.checked_div(b).ok_or_else(|| {
        std::io::Error::other("division by zero or overflow")
    })
}
```

### Write the Cells

The first cell stores the sum. The second cell loads it and runs two divisions with `Promise.allSettled`, so the failing call does not stop the other one.

```rust title="src/main.rs"
// Values passed to store() stay available to later cells in the session
const CELLS: [&str; 2] = [
    "store('sum', await tools.checked_sum({ a: 19, b: 23 })); text(load('sum'));",
    "const [half, zero] = await Promise.allSettled([\
   tools.quotient({ a: load('sum'), b: 2 }),\
   tools.quotient({ a: 1, b: 0 }),\
 ]); text({ half: half.value, zero: zero.reason });",
];
```

### Run the Cells

`ToolRuntime::new_with_tools` builds the tool runtime over the workspace. `execute_code` runs one cell, and `control().cancel()` stops the evaluator when the program ends.

```rust title="src/main.rs"
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
    // 1. Build the tool runtime with the two tools
    let tools = Tools::builder()
        .without_defaults()
        .tool(checked_sum)
        .tool(quotient)
        .build()?;
    let runtime = ToolRuntime::new_with_tools(
        std::env::current_dir()?,
        Vec::new(),
        &tools,
    );

    // 2. Run each cell in one session and print its output
    for (index, source) in CELLS.iter().enumerate() {
        let call = index.to_string();
        let context = ToolContext::new(
            "docs",
            "code-mode",
            &call,
            &[],
            usize::MAX,
        );
        let result = runtime.execute_code(source, context).await?;
        println!(
            "{}",
            serde_json::json!({
                "cell": index,
                "success": result.success,
                "output": result.output
            })
        );
    }

    runtime.control().cancel().await;
    Ok(())
}
```

The program prints one line per cell. The failing division returns its error message as the rejection reason.

```json title="Output"
{"cell":0,"output":[{"text":"Script completed\nWall time 0.0 seconds\nOutput:\n","type":"input_text"},{"text":"42","type":"input_text"}],"success":true}
{"cell":1,"output":[{"text":"Script completed\nWall time 0.0 seconds\nOutput:\n","type":"input_text"},{"text":"{\"half\":21,\"zero\":\"division by zero or overflow\"}","type":"input_text"}],"success":true}
```

## Recipes

### Use Cell Globals

A cell has these globals besides `tools`. Each value that `text()` receives is a separate output item.

* **`text(value)`:** Appends a text item. Non-string values pass through `JSON.stringify`.
* **`image(url)`:** Appends an image from a base64 `data:` URL or a provider file ID.
* **`store(key, value)`:** Keeps a serializable value for later cells in the same session.
* **`load(key)`:** Returns a stored value, or `undefined`.
* **`notify(value)`:** Sends an extra output to the model during the cell.
* **`exit()`:** Ends the cell successfully.

```ts
// Run two calls in parallel, keep one result, and return a short
// summary
const [sum, half] = await Promise.all([
  tools.checked_sum({ a: 19, b: 23 }),
  tools.quotient({ a: 84, b: 2 }),
])
store('sum', sum)
text({ sum, half })
```

### Yield Long Cells

A cell that runs past its `yield_time_ms`, 10,000 ms by default, returns a cell ID to the model and keeps running. The model calls `wait` with that ID to collect new output, or `terminate: true` to stop the cell.

```ts
// @exec: {"yield_time_ms": 2000}
// The first-line pragma asks exec to yield after two seconds
const totals = []
for (let a = 0; a < 1000; a++)
  totals.push(await tools.checked_sum({ a, b: 1 }))
text(totals.length)
```

### Choose the Exposure

Registered Rust tools default to `ToolExposure::CodeModeOnly`, so the model calls them only from cells. `ToolExposure::DirectAndCodeMode` also offers a tool as a direct tool call. nanocode-wasm has no exposure setting: each `config.tools` definition is offered directly and inside Code Mode.

#### Rust

```rust
use nanocode::Tools;
use nanocode::tools::ToolExposure;

// Offer every registered tool directly and inside Code Mode
let tools = Tools::builder()
    .without_defaults()
    .exposure(ToolExposure::DirectAndCodeMode)
    .tool(checked_sum)
    .build()?;
```

#### TypeScript

```ts
// 1. Add the two integers that the model sends
const call = async (_name: string, input: unknown) => {
  const { a, b } = input as { a: number; b: number }
  return a + b
}

// 2. Offer the tool directly and inside Code Mode, with config and
// dispatch from Getting Started
const parameters = {
  properties: { a: { type: 'integer' }, b: { type: 'integer' } },
  required: ['a', 'b'],
  type: 'object',
}
await runtime.open(
  {
    ...config,
    tools: [
      {
        description: 'Add two signed integers',
        name: 'checked_sum',
        parameters,
      },
    ],
  },
  { dispatch, tools: { call } },
)
```

Tools that edit files, such as Claude's Write and Edit and Codex's `apply_patch`, wait for running calls and hold new ones until they finish. Other calls share access, and release it while their cell is yielded.

### Run Cells in the Host

In TypeScript, `code_mode: true` runs cells in the host's own JavaScript runtime through [`LocalHand`](/docs/api/wasm/LocalHand), so a browser page, a Node server or a Durable Object gets Code Mode without a machine. Host tools become `tools.<name>()` calls inside cells, and the cell's `fetch` is the one the hand receives.

```ts
import { LocalHand } from 'nanocode-wasm'

await runtime.open(
  { ...config, code_mode: true },
  { dispatch, remote: new LocalHand({ fetch }), tools: { call } },
)
```

### Run Cells Remotely

The native evaluator is an embedded QuickJS worker. `code_mode_host(...)` on the Rust runtime builder replaces it with a remote executor, which runs cells and machine tools on another machine. The runtime then starts no local machine tools.

In TypeScript, `remote: true` runs every cell on one executor, named by `executor` (`default` if unset). `RemoteExecutor` carries the SDK's frames over a `MessageSocket` and implements the `remote` runtime option. Connect it before `open`, because `open` reads the executor's catalog.

#### Rust

```rust
use nanocode::code_mode::{
    attachment::AttachmentTarget,
    remote::{SocketPolicy, SocketTransport},
};

// 1. Connect to a runner started with cargo run --example runner --
// --listen
let policy = SocketPolicy {
    max_frame_bytes: 64 * 1024 * 1024,
    request_timeout: std::time::Duration::from_secs(120),
};
let transport = SocketTransport::connect(
    AttachmentTarget::new("ws://127.0.0.1:9000", token)?,
    policy,
)
.await?;

// 2. Run every cell on the runner
let runtime = Nanocode::builder()
    .openai(OpenAi::default())
    .code_mode_host(transport.into_host())?
    .build_async()
    .await?;
```

#### TypeScript

```ts
import { RemoteExecutor } from 'nanocode-wasm'

// 1. Dial the executor and wait for its catalog frame
const executor = await RemoteExecutor.connect(
  async () => new WebSocket(url),
  {
    max_frame_bytes: 64 * 1024 * 1024,
    timeout_ms: 120_000,
  },
)

// 2. Run every cell on the executor
await runtime.open(
  { ...config, executor: 'runner', remote: true },
  {
    dispatch,
    remote: {
      cancel: (id) => executor.cancel(id),
      dispose: () => executor.dispose(),
      execute: (request, id) => executor.execute(request, id),
      failed: () => executor.failure !== undefined,
    },
  },
)
```

`crates/nanocode/examples/brain.rs` and `runner.rs` run the Rust setup end to end. [Remote Hands](/docs/guides/remote-hands) covers machines that attach to a brain by name.

## More

- [Tools](/docs/guides/tools): Define the tools that cells call.

- [LocalHand](/docs/api/wasm/LocalHand): Reference for running cells in the host.

- [RemoteExecutor](/docs/api/wasm/RemoteExecutor): Reference for the remote Code Mode transport.

- [Remote Hands](/docs/guides/remote-hands): Run cells and tools on other machines.

- [Tools API](/docs/api/rust/Tools): Reference for exposure and Code Mode hosts.

- [hand](/docs/api/rust/hand): Reference for the hand side of remote Code Mode.
