Skip to Content
Switchboard

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_tools and get_schema are listed next to execute, so the model can plan first. Their results use the names as written in code, such as github.create_issue.
  • Inside the sandbox, await catalog.search("create issue", limit) returns matching tools, and await 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

BudgetDefaultProfile key
Wall-clock time30 secondsbudgets.timeoutMs
Tool calls per run64budgets.maxToolCalls
Memory64 MBbudgets.memoryMb
Source size64 KBbudgets.maxSourceBytes
Result size24,000 charactersbudgets.maxResultChars
Captured logs64 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.

On this page