Code mode
The execute tool, the sandbox it runs in, and its budgets.
With "surface": "code", a profile exposes one tool, execute, which takes a single argument, code. The model writes an async arrow function in plain JavaScript that calls tools and returns a result. Each server of the profile is a global object, and each of its tools a method:
async () => {
const issues = await linear.list_issues({ assignee: 'me', state: 'In Progress' });
const stale = issues.filter((i) => Date.now() - Date.parse(i.updatedAt) > 7 * 864e5);
for (const issue of stale) {
await github.create_issue({
owner: 'acme',
repo: 'web',
title: `Follow up: ${issue.title}`,
});
}
return { checked: issues.length, followUps: stale.length };
};Steps that would otherwise be separate tool calls, with every intermediate result passed through the model, happen in one call. A bare function body, or code wrapped in a Markdown fence, is accepted too.
Global and method names are the server and tool names with every character that is not valid in a JavaScript identifier replaced by _: a server named beta-2 is the global beta_2.
Finding tools
When the TypeScript declarations of every tool fit in 12,000 characters, they are included in the execute description. For larger profiles they are left out, and the model discovers tools as it goes:
search_toolsandget_schemaare listed next toexecute, so the model can plan first. Their results use the names as written in code, such asgithub.create_issue.- Inside the sandbox,
await catalog.search("create issue", limit)returns matching tools, andawait catalog.describe(["github.create_issue"])returns their TypeScript signatures.
Results
Tool results are unwrapped before your code sees them: structured content as an object, text parsed as JSON when it is JSON, other text as a string. Images and audio are replaced by a short note. A tool that returns an error throws an Error, so try/catch works as expected.
execute returns anything written with console.log (or info, warn, error, debug) under Logs:, followed by the function's return value as JSON under Result:. When a run fails, the error starts with its kind: syntax, runtime, timeout, memory, budget or aborted, followed by a hint where one helps, such as the list of server globals when a name was not found.
The sandbox
Code runs in QuickJS compiled to WebAssembly, in a fresh runtime for every call. It has no fetch, require, process, timers or file system access: the only way out is a tool call. Every tool call made from code goes through the same profile checks as a direct call, and is recorded in the audit log, so code cannot reach a tool outside its profile. Calls still pending when the run ends are cancelled.
Budgets
| Budget | Default | Profile key |
|---|---|---|
| Wall-clock time | 30 seconds | budgets.timeoutMs |
| Tool calls per run | 64 | budgets.maxToolCalls |
| Memory | 64 MB | budgets.memoryMb |
| Source size | 64 KB | budgets.maxSourceBytes |
| Result size | 24,000 characters | budgets.maxResultChars |
| Captured logs | 64 KB |
The timeout also stops tight loops such as while (true) {}. A result longer than maxResultChars is cut off, with a note asking the model to return less. Set budgets per profile in config.json:
"automation": {
"servers": ["github", "linear"],
"surface": "code",
"budgets": { "timeoutMs": 60000, "maxToolCalls": 128, "maxResultChars": 24000 }
}Each server's callTimeoutSec (120 seconds by default) still limits every single tool call.