# Code Mode Is Necessary

Agents should write programs, not click through tools one call at a time. In Code Mode the model writes a cell that calls the tools, and only what the cell returns enters its context.

**Tool calling resends every result on every model call, and the bill grows with the square of the work.** We ran one task both ways and counted every token. Code Mode used 80 times fewer tokens and finished 14 times sooner.

## TL;DR

* **80x fewer tokens:** GPT-6 Luna read 20 log files in 814,466 input tokens with tool calls, and in 10,129 with Code Mode.
* **14x faster:** the same task took 41.7 s with tool calls and 2.9 s with Code Mode.
* **Exact answers:** with tool calls Claude Haiku 5.5 counted lines by eye and warned the count could be off. In Code Mode a cell counted them exactly.
* **Flat as the task grows:** at 60 files Haiku took 274,579 tokens with tool calls and under 8,000 in Code Mode.
* **25x cheaper:** at GPT-6 Astra's prices the task cost $1.52 with tool calls and $0.06 in Code Mode.

All runs went through the Vercel AI Gateway on 2026-11-09, three times each.

## The Task

A session has one tool, `read_log`, and 20 log files of 8.7 KB each. The prompt asks which file has the most lines containing ERROR, and how many.

Each run is the same nanocode-ts session with one setting changed: `code_mode` off or on. GPT-6 Luna ran on the Codex harness and Claude Haiku 5.5 on the Claude harness.

## Without Code Mode

GPT-6 Luna reads one file per model call. Every result stays in the conversation, and every later call sends the whole conversation again.

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta
  title "Tokens sent on each model call, without Code Mode"
  x-axis "model call" [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
  y-axis "tokens in" 0 --> 80000
  bar [4049, 7522, 11027, 14478, 17897, 21369, 24809, 28365, 31883, 35445, 38899, 42348, 45782, 49322, 52742, 56135, 59596, 63041, 66487, 69914, 73356]
```

Claude Haiku 5.5 asks for all 20 files in one call. That saves round trips but not context: its second call reads 92,326 tokens, and the model then counts the lines by eye for 17 to 53 s.

## With Code Mode

The model gets one tool, `exec`, and the session's tools become functions inside it. This is the cell Claude Haiku 5.5 wrote:

```js
// 1. Read all 20 files at once, beside the tool
const names = Array.from({length: 20}, (_, i) => `app-${String(i + 1).padStart(2, "0")}.log`);
const results = await Promise.all(names.map(async (name) => {
  const text = await tools.read_log({ name });
  const s = typeof text === "string" ? text : JSON.stringify(text);
  const count = s.split("\n").filter((l) => l.includes("ERROR")).length;
  return { name, count };
}));
// 2. Return one line of JSON to the model
let best = results[0];
for (const r of results) if (r.count > best.count) best = r;
text(JSON.stringify({ results, best }));
```

**The 20 files never pass through the model.** Two model calls, 7,356 tokens in, and 2.3 s.

## The Numbers

| Run                                    | Model calls |      Tokens in |             Time |
| -------------------------------------- | ----------: | -------------: | ---------------: |
| GPT-6 Luna, tool calls                 |          21 |        814,466 |           41.7 s |
| GPT-6 Luna, Code Mode                  |           2 |         10,129 |            2.9 s |
| Claude Haiku 5.5, tool calls           |           2 |         93,905 |   21.0 to 56.1 s |
| Claude Haiku 5.5, Code Mode            |           2 | 7,356 to 7,378 |            2.3 s |
| Claude Haiku 5.5, 60 files, tool calls |           2 |        274,579 | 107.5 to 282.6 s |
| Claude Haiku 5.5, 60 files, Code Mode  |           2 | 7,707 to 7,976 |     2.8 to 3.3 s |

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "Tokens in for the whole task, thousands"
  x-axis ["Luna, tool calls", "Luna, Code Mode", "Haiku, tool calls", "Haiku, Code Mode", "Haiku 60 files, tool calls", "Haiku 60 files, Code Mode"]
  y-axis "thousand tokens" 0 --> 850
  bar [814, 10, 94, 7, 275, 8]
```

Every run named the right file. We want to be clear about one miss: in one of Luna's three runs without Code Mode, it stopped after nine files and answered anyway.

## Why It Wins

With n results of s tokens each:

| Way of working               | Tokens in                               | Model calls |
| ---------------------------- | --------------------------------------- | ----------- |
| One tool call per model call | s × n² / 2                              | n + 1       |
| Parallel tool calls          | s × n, kept for the rest of the session | 2           |
| Code Mode                    | What the cell returns                   | 2           |

Prompt caching makes resent tokens cheaper but does not change the shape. Of Luna's 814,466 tokens, 741,050 were cache reads, and the task still cost 25 times as much.

## Why Necessary?

Outside a sandbox, the rest of nanocode stands on Code Mode.

* **Composition without a machine:** a Function has no /bin. A cell composes host functions, MCP tools, and a shell in ordinary JavaScript.
* **Durability per call:** the journal records each call a cell makes and its result. Recovery replays the results and reports a running call as unknown.
* **Placement:** a call that names a [hand](/experiments/hands) runs on that machine, so one cell can span a Function and a laptop.
* **Delegation:** spawning a subagent is a call too, so one cell starts a [swarm](/experiments/the-harness-for-swarms) and collects it.

## The Engine

Cells run in QuickJS, compiled into the nanocode module. They need no eval, so they run in Workers, where eval is forbidden. Native hosts run the same engine, so a cell behaves the same in nc and in a browser tab.

Models reach for a shell first, and a Function cannot start a process. So nanocode-ts implements bash over a virtual filesystem: grep, sed, awk, jq, and curl are functions, files are rows of the journal, and python3 is [CPython on Pyodide](/experiments/harness-outside-the-sandbox#python-in-workers).

## Try It Today

Open the [Cloudflare demo](https://nanocode.xyz/cloudflare/), ask a question that needs many tool results, and read the cell the model writes. We think every agent harness will run this way. If yours does not yet, we would love to compare notes on [GitHub](https://github.com/nanocodexyz).

- [Code Mode](/docs/guides/code-mode): Run JavaScript cells against registered tools.

- [Subagents](/docs/guides/subagents): Spawn children with typed results.

- [nanocode-ts](/docs/guides/nanocode-ts): The module, its QuickJS engine, and the shell.
