# Code mode and credits (https://condensate.dev/guides/middleware/code-mode/)

> Let a model make several tool calls in one program, and count every call agents make.

Let a model make several tool calls in one short program, and count every call it makes.

## What code mode is

Code mode lets a model run one short program that calls several tools and returns a concise result. Each tool call is still a normal, metered tool call. The program only avoids a separate model round trip for each intermediate value. Credits are the prepaid balance every metered call debits. [The middleware](https://condensate.dev/guides/middleware/) explains the endpoint the calls pass through.

## Why you would want it

- A task needs tool A, then B with A's answer, then C with B's. Without code mode each step costs a model round trip, and each intermediate value lands in the model's context. With it, one program makes the three calls and returns only the part the model needs.
- You want to know what agents spend. Every call, inside a program or not, debits the same credits, and a failed call is refunded.
- A caller you have not allowed should see nothing. Only the callers you list see the code-mode tools.

## How it works

```text
Without batching:
model → tool A → model → tool B → model → tool C → model

With a code-mode program:
model → program calls A, B, C → selected result → model
```

This is an execution sketch, not code to paste into the sandbox. The model writes one JavaScript program and hands it to `codemode__run`. The program runs in a QuickJS sandbox under fixed caps. Inside it, `call` reaches any aggregated tool, and each such call is checked and metered as if the model had made it. The program ends with `return`, and only that value goes back to the model.

## Try it

Code mode is off by default. To switch it on, set `CONDENSATE_CODE_MODE` to `opt-in` or `internal` (`off`, or leaving it unset, keeps it off), and list the callers who may use it in `CONDENSATE_CODE_MODE_PRINCIPALS` as a comma-separated list. Under either value, only the listed callers see the code-mode tools. Other callers see nothing. The two values behave the same today. Any other value stops the backend from booting, so a typo cannot silently switch a rollout off.

You should see three tools under the `codemode` upstream in `tools/list` for a listed caller:

| Tool | What it does |
| --- | --- |
| `codemode__search` | Finds tools and namespace methods by words describing the task, best match first. `limit` runs from 1 to 20, default 8. |
| `codemode__types` | Returns TypeScript signatures for the merged tool surface. `docs: true` adds JSDoc descriptions. |
| `codemode__run` | Runs one JavaScript program in a QuickJS sandbox. Inside it, `await call("<upstream>__<tool>", args)` calls any aggregated tool. The program ends with `return`, and only that value comes back. |

## What happens when

### No caller is listed

With no callers listed, the backend warns at boot and nobody sees the tools.

### A program hits a cap

Programs run under fixed caps. Each cap returns a typed error that names it, so nothing is cut off silently. [Reference](#reference) lists the caps.

### Two programs run at once

Only one run is allowed at a time per caller.

### A program calls a native namespace

Native namespaces, such as `agents`, `canvas` and `review`, are host objects rather than tools. They are bound only when their service URL is configured, and they count toward the namespace-call cap, not the tool-call cap.

### A head reads the threads below it

A head is the lead agent, the one that hands tasks to other agents. A head that runs a program in its own thread can read the threads below it without raw HTTP. `agents.children` and `agents.history` answer one page at a time and say when more follow. `agents.turnStatus` and `agents.memory.reflect` cap their lists and count what they left out. `agents.memory.recall` answers at most 20 memories. Like every other `agents` method that takes a thread id, the four thread reads reach only the thread the program runs in, a thread below it, or a thread the program opened. The memory reads take no thread: they read the workspace the aggregator is set to.

| Method | What it answers |
| --- | --- |
| `agents.thread(id)` | The thread's type, title, parent, stage and whether its agent is replying. |
| `agents.children(id?, { after, limit })` | One page of the live (not archived) direct children, newest first. With no id it lists the calling thread's children. It never lists a sibling or an ancestor. |
| `agents.turnStatus(id)` | Whether the agent is replying, and which messages wait. |
| `agents.history(id, { after, limit })` | The log from its first event. It never skips older events in favour of a snapshot. |
| `agents.memory.recall(query, { limit, tags })` | Memories that match the query, from the aggregator's workspace. |
| `agents.memory.reflect(query)` | A written answer from that workspace's memory, with its sources. |

The memory reads are on only when `CONDENSATE_AGENTS_WORKSPACE` names a workspace. Without it, they are refused.

### You search for a tool

`codemode__search` ranks every aggregated tool and every method of a namespace in scope, such as `agents.spawn` or `review.pr`. A match on more of your words ranks first, so a tool that matches every word comes before one that matches only some. Among matches on the same number of words, a word in the name counts for more than a word in the description. A plural finds its singular, so "threads" finds "thread". When nothing matches, the answer says where to look next.

## Credits and metering

When `/mcp` is on, every `tools/call` debits prepaid credits. A new caller receives a signup grant. A failed metered call is refunded.

- `CONDENSATE_FREE_SIGNUP_CREDITS` sets the signup grant.
- `CONDENSATE_DEFAULT_TOOL_COST` sets the cost per call, which is 1 by default.
- `CONDENSATE_TOOL_COSTS` overrides the cost for named tools.
- Balances live in Postgres when `CONDENSATE_DATABASE_URL` is set. Otherwise they live in a JSONL file at `CONDENSATE_CREDITS_PATH`. With neither, they stay in memory, the backend logs a warning, and balances reset on restart.

## Reference

| Cap | Default |
| --- | --- |
| Wall-clock time per run | 10 seconds. Some surfaces raise this for a longer run. |
| Tool calls per run | 20 |
| Namespace calls per run | 100 |
| Reply timeout per tool call | 180 seconds |
| Returned result size | 64 KiB of JSON |
| `agents.children` page | 20 threads, at most 50 |
| `agents.history` page | 50 events, at most 200 |
| `agents.turnStatus` queue | 50 listed; the rest are counted |
| `agents.memory.recall` hits | 8, at most 20 |
| `agents.memory.reflect` sources and documents | 20 of each listed; the rest are counted |

| Search answer field | What it holds |
| --- | --- |
| `kind` | `tool` for an aggregated tool, `namespace` for a namespace method. |
| `upstream` | The upstream of a `tool` match. Pass it as `upstream` to search that upstream only. |
| `namespace` | The namespace of a `namespace` match, with the method's one-line description. Pass it as `upstream` to search that namespace only. |
| `hint` | Present only when nothing matched. It names `codemode__types` and the namespaces in scope. |

A query is at most 200 characters, and only its first 16 distinct words are scored.

| Setting | What it does |
| --- | --- |
| `CONDENSATE_CODE_MODE` | `off` (default), `opt-in` or `internal`. Any other value stops the boot. |
| `CONDENSATE_CODE_MODE_PRINCIPALS` | The comma-separated callers who see the code-mode tools. |
| `CONDENSATE_FREE_SIGNUP_CREDITS` | The signup grant for a new caller. |
| `CONDENSATE_DEFAULT_TOOL_COST` | The cost per call. Default 1. |
| `CONDENSATE_TOOL_COSTS` | The cost of named tools. |
| `CONDENSATE_DATABASE_URL` | Keeps balances in Postgres. |
| `CONDENSATE_CREDITS_PATH` | Keeps balances in a JSONL file when there is no database. |
| `CONDENSATE_AGENTS_WORKSPACE` | The workspace whose memory `agents.memory` reads. Unset, those reads are refused. |

## Read next

- [Connect an upstream](https://condensate.dev/guides/middleware/upstreams/), for the endpoint these tools are served from and the `lean` tool list that pairs with code mode.
- [Hook up Claude Code and Codex](https://condensate.dev/guides/connect-agents/), whose plugin connects the code-mode tools for those two agents.

## Implementation

- The code-mode tools, the sandbox and its caps are in `apps/middleware/backend/src/mcp/codemode.ts`.
- The code-mode and credit settings are read in `apps/middleware/backend/index.ts`.
- The `agents` and `steps` namespaces, their reach rule and their page caps are in `apps/middleware/backend/src/mcp/codemode-agents.ts`. The types `codemode__types` prints are in `codemode-agents-dts.ts` beside it.
