Tools: add xAI-backed code_execution

This commit is contained in:
huntharo 2026-03-27 22:24:40 -04:00 committed by Peter Steinberger
parent 1c9684608a
commit 396bf20cc6
26 changed files with 1074 additions and 19 deletions

View File

@ -60229,6 +60229,124 @@
"tags": [],
"hasChildren": false
},
{
"path": "tools.code_execution",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "tools.code_execution.apiKey",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"auth",
"security",
"tools"
],
"label": "xAI API Key",
"help": "xAI API key for remote code execution (fallback: XAI_API_KEY env var).",
"hasChildren": true
},
{
"path": "tools.code_execution.apiKey.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "tools.code_execution.apiKey.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "tools.code_execution.apiKey.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "tools.code_execution.enabled",
"kind": "core",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"tools"
],
"label": "Enable Code Execution Tool",
"help": "Enable the code_execution tool (requires XAI_API_KEY or tools.code_execution.apiKey).",
"hasChildren": false
},
{
"path": "tools.code_execution.maxTurns",
"kind": "core",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"performance",
"tools"
],
"label": "Code Execution Max Turns",
"help": "Optional max internal tool turns xAI may use per code_execution request. Omit to let xAI choose.",
"hasChildren": false
},
{
"path": "tools.code_execution.model",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models",
"tools"
],
"label": "Code Execution Model",
"help": "Model to use for remote code execution (default: \"grok-4-1-fast\").",
"hasChildren": false
},
{
"path": "tools.code_execution.timeoutSeconds",
"kind": "core",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"performance",
"tools"
],
"label": "Code Execution Timeout (sec)",
"help": "Timeout in seconds for code_execution requests.",
"hasChildren": false
},
{
"path": "tools.deny",
"kind": "core",
@ -64706,6 +64824,154 @@
"help": "Timeout in seconds for web_search requests.",
"hasChildren": false
},
{
"path": "tools.web.x_search",
"kind": "core",
"type": "object",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": true
},
{
"path": "tools.web.x_search.apiKey",
"kind": "core",
"type": [
"object",
"string"
],
"required": false,
"deprecated": false,
"sensitive": true,
"tags": [
"auth",
"security",
"tools"
],
"label": "xAI API Key",
"help": "xAI API key for X search (fallback: XAI_API_KEY env var).",
"hasChildren": true
},
{
"path": "tools.web.x_search.apiKey.id",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "tools.web.x_search.apiKey.provider",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "tools.web.x_search.apiKey.source",
"kind": "core",
"type": "string",
"required": true,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "tools.web.x_search.cacheTtlMinutes",
"kind": "core",
"type": "number",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"performance",
"storage",
"tools"
],
"label": "X Search Cache TTL (min)",
"help": "Cache TTL in minutes for x_search results.",
"hasChildren": false
},
{
"path": "tools.web.x_search.enabled",
"kind": "core",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"tools"
],
"label": "Enable X Search Tool",
"help": "Enable the x_search tool (requires XAI_API_KEY or tools.web.x_search.apiKey).",
"hasChildren": false
},
{
"path": "tools.web.x_search.inlineCitations",
"kind": "core",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"tools"
],
"label": "X Search Inline Citations",
"help": "Keep inline citations from xAI in x_search responses when available (default: false).",
"hasChildren": false
},
{
"path": "tools.web.x_search.maxTurns",
"kind": "core",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"performance",
"tools"
],
"label": "X Search Max Turns",
"help": "Optional max internal search/tool turns xAI may use per x_search request. Omit to let xAI choose.",
"hasChildren": false
},
{
"path": "tools.web.x_search.model",
"kind": "core",
"type": "string",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"models",
"tools"
],
"label": "X Search Model",
"help": "Model to use for X search (default: \"grok-4-1-fast-non-reasoning\").",
"hasChildren": false
},
{
"path": "tools.web.x_search.timeoutSeconds",
"kind": "core",
"type": "integer",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [
"performance",
"tools"
],
"label": "X Search Timeout (sec)",
"help": "Timeout in seconds for x_search requests.",
"hasChildren": false
},
{
"path": "ui",
"kind": "core",

View File

@ -1,4 +1,4 @@
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5554}
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5574}
{"recordType":"path","path":"acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACP","help":"ACP runtime controls for enabling dispatch, selecting backends, constraining allowed agent targets, and tuning streamed turn projection behavior.","hasChildren":true}
{"recordType":"path","path":"acp.allowedAgents","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"ACP Allowed Agents","help":"Allowlist of ACP target agent ids permitted for ACP runtime sessions. Empty means no additional allowlist restriction.","hasChildren":true}
{"recordType":"path","path":"acp.allowedAgents.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@ -5134,6 +5134,15 @@
{"recordType":"path","path":"tools.byProvider.*.deny","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"tools.byProvider.*.deny.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.byProvider.*.profile","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.code_execution","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"tools.code_execution.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"label":"xAI API Key","help":"xAI API key for remote code execution (fallback: XAI_API_KEY env var).","hasChildren":true}
{"recordType":"path","path":"tools.code_execution.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.code_execution.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.code_execution.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.code_execution.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable Code Execution Tool","help":"Enable the code_execution tool (requires XAI_API_KEY or tools.code_execution.apiKey).","hasChildren":false}
{"recordType":"path","path":"tools.code_execution.maxTurns","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Code Execution Max Turns","help":"Optional max internal tool turns xAI may use per code_execution request. Omit to let xAI choose.","hasChildren":false}
{"recordType":"path","path":"tools.code_execution.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models","tools"],"label":"Code Execution Model","help":"Model to use for remote code execution (default: \"grok-4-1-fast\").","hasChildren":false}
{"recordType":"path","path":"tools.code_execution.timeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Code Execution Timeout (sec)","help":"Timeout in seconds for code_execution requests.","hasChildren":false}
{"recordType":"path","path":"tools.deny","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access","tools"],"label":"Tool Denylist","help":"Global tool denylist that blocks listed tools even when profile or provider rules would allow them. Use deny rules for emergency lockouts and long-term defense-in-depth.","hasChildren":true}
{"recordType":"path","path":"tools.deny.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.elevated","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Elevated Tool Access","help":"Elevated tool access controls for privileged command surfaces that should only be reachable from trusted senders. Keep disabled unless operator workflows explicitly require elevated actions.","hasChildren":true}
@ -5525,6 +5534,17 @@
{"recordType":"path","path":"tools.web.search.perplexity.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.web.search.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Web Search Provider","help":"Search provider id. Auto-detected from available API keys if omitted.","hasChildren":false}
{"recordType":"path","path":"tools.web.search.timeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Search Timeout (sec)","help":"Timeout in seconds for web_search requests.","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"tools.web.x_search.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"label":"xAI API Key","help":"xAI API key for X search (fallback: XAI_API_KEY env var).","hasChildren":true}
{"recordType":"path","path":"tools.web.x_search.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.cacheTtlMinutes","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance","storage","tools"],"label":"X Search Cache TTL (min)","help":"Cache TTL in minutes for x_search results.","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable X Search Tool","help":"Enable the x_search tool (requires XAI_API_KEY or tools.web.x_search.apiKey).","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.inlineCitations","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"X Search Inline Citations","help":"Keep inline citations from xAI in x_search responses when available (default: false).","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.maxTurns","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"X Search Max Turns","help":"Optional max internal search/tool turns xAI may use per x_search request. Omit to let xAI choose.","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models","tools"],"label":"X Search Model","help":"Model to use for X search (default: \"grok-4-1-fast-non-reasoning\").","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.timeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"X Search Timeout (sec)","help":"Timeout in seconds for x_search requests.","hasChildren":false}
{"recordType":"path","path":"ui","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"UI","help":"UI presentation settings for accenting and assistant identity shown in control surfaces. Use this for branding and readability customization without changing runtime behavior.","hasChildren":true}
{"recordType":"path","path":"ui.assistant","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Assistant Appearance","help":"Assistant display identity settings for name and avatar shown in UI surfaces. Keep these values aligned with your operator-facing persona and support expectations.","hasChildren":true}
{"recordType":"path","path":"ui.assistant.avatar","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Assistant Avatar","help":"Assistant avatar image source used in UI surfaces (URL, path, or data URI depending on runtime support). Use trusted assets and consistent branding dimensions for clean rendering.","hasChildren":false}

View File

@ -1146,6 +1146,7 @@
]
},
"tools/btw",
"tools/code-execution",
"tools/diffs",
"tools/elevated",
"tools/exec",

View File

@ -28,7 +28,8 @@ openclaw onboard --auth-choice xai-api-key
```
OpenClaw now uses the xAI Responses API as the bundled xAI transport. The same
`XAI_API_KEY` can also power Grok-backed `web_search` and first-class `x_search`.
`XAI_API_KEY` can also power Grok-backed `web_search`, first-class `x_search`,
and remote `code_execution`.
If you store an xAI key under `plugins.entries.xai.config.webSearch.apiKey`,
the bundled xAI model provider now reuses that key as a fallback too.
@ -61,5 +62,6 @@ openclaw config set tools.web.search.provider grok
## Notes
- OpenClaw applies xAI-specific tool-schema and tool-call compatibility fixes automatically on the shared runner path.
- `web_search` and `x_search` are exposed as OpenClaw tools. OpenClaw enables the specific xAI built-in it needs inside each tool request instead of attaching both search tools to every chat turn.
- `web_search`, `x_search`, and `code_execution` are exposed as OpenClaw tools. OpenClaw enables the specific xAI built-in it needs inside each tool request instead of attaching all native tools to every chat turn.
- `code_execution` is remote xAI sandbox execution, not local [`exec`](/tools/exec).
- For the broader provider overview, see [Model providers](/providers/index).

View File

@ -0,0 +1,83 @@
---
summary: "code_execution -- run sandboxed remote Python analysis with xAI"
read_when:
- You want to enable or configure code_execution
- You want remote analysis without local shell access
- You want to combine x_search or web_search with remote Python analysis
title: "Code Execution"
---
# Code Execution
`code_execution` runs sandboxed remote Python analysis on xAI's Responses API.
This is different from local [`exec`](/tools/exec):
- `exec` runs shell commands on your machine or node
- `code_execution` runs Python in xAI's remote sandbox
Use `code_execution` for:
- calculations
- tabulation
- quick statistics
- chart-style analysis
- analyzing data returned by `x_search` or `web_search`
Do **not** use it when you need local files, your shell, your repo, or paired
devices. Use [`exec`](/tools/exec) for that.
## Setup
You need an xAI API key. Any of these work:
- `tools.code_execution.apiKey`
- `XAI_API_KEY`
- `plugins.entries.xai.config.webSearch.apiKey`
Example:
```json5
{
tools: {
code_execution: {
enabled: true,
apiKey: "xai-...",
model: "grok-4-1-fast",
maxTurns: 2,
timeoutSeconds: 30,
},
},
}
```
## How To Use It
Ask naturally and make the analysis intent explicit:
```text
Use code_execution to calculate the 7-day moving average for these numbers: ...
```
```text
Use x_search to find posts mentioning OpenClaw this week, then use code_execution to count them by day.
```
```text
Use web_search to gather the latest AI benchmark numbers, then use code_execution to compare percent changes.
```
The tool takes a single `task` parameter internally, so the agent should send
the full analysis request and any inline data in one prompt.
## Limits
- This is remote xAI execution, not local process execution.
- It should be treated as ephemeral analysis, not a persistent notebook.
- Do not assume access to local files or your workspace.
- For fresh X data, use [`x_search`](/tools/web#x_search) first.
## See Also
- [Web tools](/tools/web)
- [Exec](/tools/exec)
- [xAI](/providers/xai)

View File

@ -52,19 +52,20 @@ OpenClaw has three layers that work together:
These tools ship with OpenClaw and are available without installing any plugins:
| Tool | What it does | Page |
| --------------------------------------- | -------------------------------------------------------- | --------------------------------- |
| `exec` / `process` | Run shell commands, manage background processes | [Exec](/tools/exec) |
| `browser` | Control a Chromium browser (navigate, click, screenshot) | [Browser](/tools/browser) |
| `web_search` / `x_search` / `web_fetch` | Search the web, search X posts, fetch page content | [Web](/tools/web) |
| `read` / `write` / `edit` | File I/O in the workspace | |
| `apply_patch` | Multi-hunk file patches | [Apply Patch](/tools/apply-patch) |
| `message` | Send messages across all channels | [Agent Send](/tools/agent-send) |
| `canvas` | Drive node Canvas (present, eval, snapshot) | |
| `nodes` | Discover and target paired devices | |
| `cron` / `gateway` | Manage scheduled jobs, restart gateway | |
| `image` / `image_generate` | Analyze or generate images | |
| `sessions_*` / `agents_list` | Session management, sub-agents | [Sub-agents](/tools/subagents) |
| Tool | What it does | Page |
| --------------------------------------- | -------------------------------------------------------- | --------------------------------------- |
| `exec` / `process` | Run shell commands, manage background processes | [Exec](/tools/exec) |
| `code_execution` | Run sandboxed remote Python analysis with xAI | [Code Execution](/tools/code-execution) |
| `browser` | Control a Chromium browser (navigate, click, screenshot) | [Browser](/tools/browser) |
| `web_search` / `x_search` / `web_fetch` | Search the web, search X posts, fetch page content | [Web](/tools/web) |
| `read` / `write` / `edit` | File I/O in the workspace | |
| `apply_patch` | Multi-hunk file patches | [Apply Patch](/tools/apply-patch) |
| `message` | Send messages across all channels | [Agent Send](/tools/agent-send) |
| `canvas` | Drive node Canvas (present, eval, snapshot) | |
| `nodes` | Discover and target paired devices | |
| `cron` / `gateway` | Manage scheduled jobs, restart gateway | |
| `image` / `image_generate` | Analyze or generate images | |
| `sessions_*` / `agents_list` | Session management, sub-agents | [Sub-agents](/tools/subagents) |
For image work, use `image` for analysis and `image_generate` for generation or editing. If you target `openai/*`, `google/*`, `fal/*`, or another non-default image provider, configure that provider's auth/API key first.
@ -111,7 +112,7 @@ Use `group:*` shorthands in allow/deny lists:
| Group | Tools |
| ------------------ | --------------------------------------------------------------------------------------------------------- |
| `group:runtime` | exec, bash, process |
| `group:runtime` | exec, bash, process, code_execution |
| `group:fs` | read, write, edit, apply_patch |
| `group:sessions` | sessions_list, sessions_history, sessions_send, sessions_spawn, sessions_yield, subagents, session_status |
| `group:memory` | memory_search, memory_get |

View File

@ -0,0 +1,129 @@
import { normalizeXaiModelId } from "openclaw/plugin-sdk/provider-models";
import { postTrustedWebToolsJson } from "openclaw/plugin-sdk/provider-web-search";
import { extractXaiWebSearchContent, type XaiWebSearchResponse } from "./web-search-shared.js";
export const XAI_CODE_EXECUTION_ENDPOINT = "https://api.x.ai/v1/responses";
export const XAI_DEFAULT_CODE_EXECUTION_MODEL = "grok-4-1-fast";
export type XaiCodeExecutionConfig = {
apiKey?: unknown;
model?: unknown;
maxTurns?: unknown;
};
export type XaiCodeExecutionResponse = XaiWebSearchResponse & {
output?: Array<{
type?: string;
}>;
};
export type XaiCodeExecutionResult = {
content: string;
citations: string[];
usedCodeExecution: boolean;
outputTypes: string[];
};
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
export function resolveXaiCodeExecutionConfig(
config?: Record<string, unknown>,
): XaiCodeExecutionConfig {
return isRecord(config) ? (config as XaiCodeExecutionConfig) : {};
}
export function resolveXaiCodeExecutionModel(config?: Record<string, unknown>): string {
const resolved = resolveXaiCodeExecutionConfig(config);
return typeof resolved.model === "string" && resolved.model.trim()
? normalizeXaiModelId(resolved.model.trim())
: XAI_DEFAULT_CODE_EXECUTION_MODEL;
}
export function resolveXaiCodeExecutionMaxTurns(
config?: Record<string, unknown>,
): number | undefined {
const raw = resolveXaiCodeExecutionConfig(config).maxTurns;
if (typeof raw !== "number" || !Number.isFinite(raw)) {
return undefined;
}
const normalized = Math.trunc(raw);
return normalized > 0 ? normalized : undefined;
}
export function buildXaiCodeExecutionPayload(params: {
task: string;
model: string;
tookMs: number;
content: string;
citations: string[];
usedCodeExecution: boolean;
outputTypes: string[];
}): Record<string, unknown> {
return {
task: params.task,
provider: "xai",
model: params.model,
tookMs: params.tookMs,
content: params.content,
citations: params.citations,
usedCodeExecution: params.usedCodeExecution,
outputTypes: params.outputTypes,
};
}
export async function requestXaiCodeExecution(params: {
apiKey: string;
model: string;
timeoutSeconds: number;
maxTurns?: number;
task: string;
}): Promise<XaiCodeExecutionResult> {
return await postTrustedWebToolsJson(
{
url: XAI_CODE_EXECUTION_ENDPOINT,
timeoutSeconds: params.timeoutSeconds,
apiKey: params.apiKey,
body: {
model: params.model,
input: [{ role: "user", content: params.task }],
tools: [{ type: "code_interpreter" }],
...(params.maxTurns ? { max_turns: params.maxTurns } : {}),
},
errorLabel: "xAI",
},
async (response) => {
const data = (await response.json()) as XaiCodeExecutionResponse;
const { text, annotationCitations } = extractXaiWebSearchContent(data);
const outputTypes = Array.isArray(data.output)
? [
...new Set(
data.output
.map((entry) => entry?.type)
.filter((value): value is string => Boolean(value)),
),
]
: [];
const citations =
Array.isArray(data.citations) && data.citations.length > 0
? data.citations
: annotationCitations;
return {
content: text ?? "No response",
citations,
usedCodeExecution: outputTypes.includes("code_interpreter_call"),
outputTypes,
};
},
);
}
export const __testing = {
buildXaiCodeExecutionPayload,
requestXaiCodeExecution,
resolveXaiCodeExecutionConfig,
resolveXaiCodeExecutionMaxTurns,
resolveXaiCodeExecutionModel,
XAI_DEFAULT_CODE_EXECUTION_MODEL,
} as const;

View File

@ -27,7 +27,12 @@ import { createSessionsSpawnTool } from "./tools/sessions-spawn-tool.js";
import { createSessionsYieldTool } from "./tools/sessions-yield-tool.js";
import { createSubagentsTool } from "./tools/subagents-tool.js";
import { createTtsTool } from "./tools/tts-tool.js";
import { createWebFetchTool, createWebSearchTool, createXSearchTool } from "./tools/web-tools.js";
import {
createCodeExecutionTool,
createWebFetchTool,
createWebSearchTool,
createXSearchTool,
} from "./tools/web-tools.js";
import { resolveWorkspaceRoot } from "./workspace-dir.js";
type OpenClawToolsDeps = {
@ -159,6 +164,9 @@ export function createOpenClawTools(
config: options?.config,
runtimeXSearch: runtimeWebTools?.xSearch,
});
const codeExecutionTool = createCodeExecutionTool({
config: options?.config,
});
const webFetchTool = createWebFetchTool({
config: options?.config,
sandboxed: options?.sandboxed,
@ -256,6 +264,7 @@ export function createOpenClawTools(
}),
...(webSearchTool ? [webSearchTool] : []),
...(xSearchTool ? [xSearchTool] : []),
...(codeExecutionTool ? [codeExecutionTool] : []),
...(webFetchTool ? [webFetchTool] : []),
...(imageTool ? [imageTool] : []),
...(pdfTool ? [pdfTool] : []),

View File

@ -282,4 +282,50 @@ describe("openclaw tools runtime web metadata wiring", () => {
"https://x.com/openclaw/status/1",
]);
});
it("resolves code_execution SecretRef from the active runtime snapshot", async () => {
const snapshot = await prepareAndActivate({
config: asConfig({
tools: {
code_execution: {
apiKey: { source: "env", provider: "default", id: "CODE_EXECUTION_RUNTIME_REF" },
},
},
}),
env: {
CODE_EXECUTION_RUNTIME_REF: "code-execution-runtime-key",
},
});
const mockFetch = vi.fn((_input?: unknown, _init?: unknown) =>
Promise.resolve({
ok: true,
json: () =>
Promise.resolve({
output: [
{ type: "code_interpreter_call" },
{
type: "message",
content: [{ type: "output_text", text: "runtime code execution ok" }],
},
],
}),
} as Response),
);
global.fetch = withFetchPreconnect(mockFetch);
const codeExecution = findTool("code_execution", snapshot.config);
const result = await codeExecution.execute("call-runtime-code-execution", {
task: "Add 20 + 22",
});
expect(mockFetch).toHaveBeenCalled();
expect(String(mockFetch.mock.calls[0]?.[0])).toContain("api.x.ai/v1/responses");
const request = mockFetch.mock.calls[0]?.[1] as RequestInit | undefined;
const body = JSON.parse(typeof request?.body === "string" ? request.body : "{}") as {
tools?: Array<Record<string, unknown>>;
};
expect(body.tools).toEqual([{ type: "code_interpreter" }]);
expect((result.details as { usedCodeExecution?: boolean }).usedCodeExecution).toBe(true);
});
});

View File

@ -233,6 +233,8 @@ export function buildAgentSystemPrompt(params: {
ls: "List directory contents",
exec: "Run shell commands (pty available for TTY-required CLIs)",
process: "Manage background exec sessions",
code_execution:
"Run sandboxed remote Python analysis with xAI (no local shell or filesystem access)",
web_search: "Search the web",
x_search:
"Search X (formerly Twitter) posts with xAI, including targeted post or thread lookups; for per-post stats use the exact post URL or status ID when possible",
@ -270,6 +272,7 @@ export function buildAgentSystemPrompt(params: {
"ls",
"exec",
"process",
"code_execution",
"web_search",
"web_fetch",
"browser",

View File

@ -2,9 +2,10 @@ import { describe, expect, it } from "vitest";
import { resolveCoreToolProfilePolicy } from "./tool-catalog.js";
describe("tool-catalog", () => {
it("includes web_search, x_search, and web_fetch in the coding profile policy", () => {
it("includes code_execution, web_search, x_search, and web_fetch in the coding profile policy", () => {
const policy = resolveCoreToolProfilePolicy("coding");
expect(policy).toBeDefined();
expect(policy!.allow).toContain("code_execution");
expect(policy!.allow).toContain("web_search");
expect(policy!.allow).toContain("x_search");
expect(policy!.allow).toContain("web_fetch");

View File

@ -81,6 +81,14 @@ const CORE_TOOL_DEFINITIONS: CoreToolDefinition[] = [
sectionId: "runtime",
profiles: ["coding"],
},
{
id: "code_execution",
label: "code_execution",
description: "Run sandboxed remote analysis with xAI",
sectionId: "runtime",
profiles: ["coding"],
includeInOpenClawGroup: true,
},
{
id: "web_search",
label: "web_search",

View File

@ -92,6 +92,11 @@
"title": "Web Fetch",
"detailKeys": ["url", "extractMode", "maxChars"]
},
"code_execution": {
"emoji": "🧮",
"title": "Code Execution",
"detailKeys": ["task"]
},
"message": {
"emoji": "✉️",
"title": "Message",

View File

@ -0,0 +1,148 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { createCodeExecutionTool } from "./code-execution.js";
function installCodeExecutionFetch(payload?: Record<string, unknown>) {
const mockFetch = vi.fn((_input?: unknown, _init?: unknown) =>
Promise.resolve({
ok: true,
json: () =>
Promise.resolve(
payload ?? {
output: [
{ type: "code_interpreter_call" },
{
type: "message",
content: [
{
type: "output_text",
text: "The moving average is 42.",
},
],
},
],
},
),
} as Response),
);
global.fetch = withFetchPreconnect(mockFetch);
return mockFetch;
}
function parseFirstRequestBody(mockFetch: ReturnType<typeof installCodeExecutionFetch>) {
const request = mockFetch.mock.calls[0]?.[1] as RequestInit | undefined;
const requestBody = request?.body;
return JSON.parse(typeof requestBody === "string" ? requestBody : "{}") as Record<
string,
unknown
>;
}
afterEach(() => {
vi.restoreAllMocks();
});
describe("code_execution tool", () => {
it("enables code_execution when the xAI plugin web search key is configured", () => {
const tool = createCodeExecutionTool({
config: {
plugins: {
entries: {
xai: {
config: {
webSearch: {
apiKey: "xai-plugin-key", // pragma: allowlist secret
},
},
},
},
},
},
});
expect(tool?.name).toBe("code_execution");
});
it("uses the xAI Responses code_interpreter tool", async () => {
const mockFetch = installCodeExecutionFetch();
const tool = createCodeExecutionTool({
config: {
tools: {
code_execution: {
apiKey: "xai-config-test", // pragma: allowlist secret
model: "grok-4-1-fast",
maxTurns: 2,
},
},
},
});
const result = await tool?.execute?.("code-exec:1", {
task: "Calculate the average of 40, 42, and 44.",
});
expect(mockFetch).toHaveBeenCalled();
expect(String(mockFetch.mock.calls[0]?.[0])).toContain("api.x.ai/v1/responses");
const body = parseFirstRequestBody(mockFetch);
expect(body.model).toBe("grok-4-1-fast");
expect(body.max_turns).toBe(2);
expect(body.tools).toEqual([{ type: "code_interpreter" }]);
expect(
(result?.details as { usedCodeExecution?: boolean } | undefined)?.usedCodeExecution,
).toBe(true);
});
it("reuses the xAI plugin web search key for code_execution requests", async () => {
const mockFetch = installCodeExecutionFetch();
const tool = createCodeExecutionTool({
config: {
plugins: {
entries: {
xai: {
config: {
webSearch: {
apiKey: "xai-plugin-key", // pragma: allowlist secret
},
},
},
},
},
},
});
await tool?.execute?.("code-exec:plugin-key", {
task: "Sum 1 + 2 + 3.",
});
const request = mockFetch.mock.calls[0]?.[1] as RequestInit | undefined;
expect((request?.headers as Record<string, string> | undefined)?.Authorization).toBe(
"Bearer xai-plugin-key",
);
});
it("reuses the legacy grok web search key for code_execution requests", async () => {
const mockFetch = installCodeExecutionFetch();
const tool = createCodeExecutionTool({
config: {
tools: {
web: {
search: {
grok: {
apiKey: "xai-legacy-key", // pragma: allowlist secret
},
},
},
},
},
});
await tool?.execute?.("code-exec:legacy-key", {
task: "Multiply 6 * 7.",
});
const request = mockFetch.mock.calls[0]?.[1] as RequestInit | undefined;
expect((request?.headers as Record<string, string> | undefined)?.Authorization).toBe(
"Bearer xai-legacy-key",
);
});
});

View File

@ -0,0 +1,133 @@
import { Type } from "@sinclair/typebox";
import {
buildXaiCodeExecutionPayload,
requestXaiCodeExecution,
resolveXaiCodeExecutionMaxTurns,
resolveXaiCodeExecutionModel,
} from "../../../extensions/xai/src/code-execution-shared.js";
import type { OpenClawConfig } from "../../config/config.js";
import { resolveProviderWebSearchPluginConfig } from "../../plugin-sdk/provider-web-search.js";
import { jsonResult, readStringParam } from "./common.js";
import { readConfiguredSecretString, readProviderEnvValue } from "./web-search-provider-common.js";
type CodeExecutionConfig =
NonNullable<OpenClawConfig["tools"]> extends infer Tools
? Tools extends { code_execution?: infer CodeExecution }
? CodeExecution
: undefined
: undefined;
function readLegacyGrokApiKey(cfg?: OpenClawConfig): string | undefined {
const search = cfg?.tools?.web?.search;
if (!search || typeof search !== "object") {
return undefined;
}
const grok = (search as Record<string, unknown>).grok;
return readConfiguredSecretString(
grok && typeof grok === "object" ? (grok as Record<string, unknown>).apiKey : undefined,
"tools.web.search.grok.apiKey",
);
}
function readPluginXaiWebSearchApiKey(cfg?: OpenClawConfig): string | undefined {
return readConfiguredSecretString(
resolveProviderWebSearchPluginConfig(cfg as Record<string, unknown> | undefined, "xai")?.apiKey,
"plugins.entries.xai.config.webSearch.apiKey",
);
}
function resolveFallbackXaiApiKey(cfg?: OpenClawConfig): string | undefined {
return readPluginXaiWebSearchApiKey(cfg) ?? readLegacyGrokApiKey(cfg);
}
function resolveCodeExecutionConfig(cfg?: OpenClawConfig): CodeExecutionConfig | undefined {
const codeExecution = cfg?.tools?.code_execution;
if (!codeExecution || typeof codeExecution !== "object") {
return undefined;
}
return codeExecution;
}
function resolveCodeExecutionEnabled(params: {
cfg?: OpenClawConfig;
config?: CodeExecutionConfig;
}): boolean {
if (params.config?.enabled === false) {
return false;
}
const configuredApiKey = readConfiguredSecretString(
params.config?.apiKey,
"tools.code_execution.apiKey",
);
return Boolean(
configuredApiKey ||
resolveFallbackXaiApiKey(params.cfg) ||
readProviderEnvValue(["XAI_API_KEY"]),
);
}
function resolveCodeExecutionApiKey(
config?: CodeExecutionConfig,
cfg?: OpenClawConfig,
): string | undefined {
return (
readConfiguredSecretString(config?.apiKey, "tools.code_execution.apiKey") ??
resolveFallbackXaiApiKey(cfg) ??
readProviderEnvValue(["XAI_API_KEY"])
);
}
export function createCodeExecutionTool(options?: { config?: OpenClawConfig }) {
const codeExecutionConfig = resolveCodeExecutionConfig(options?.config);
if (!resolveCodeExecutionEnabled({ cfg: options?.config, config: codeExecutionConfig })) {
return null;
}
return {
label: "Code Execution",
name: "code_execution",
description:
"Run sandboxed Python analysis with xAI. Use for calculations, tabulation, summaries, and chart-style analysis without local machine access.",
parameters: Type.Object({
task: Type.String({
description:
"The full analysis task for xAI's remote Python sandbox. Include any data to analyze directly in the task.",
}),
}),
execute: async (_toolCallId: string, args: Record<string, unknown>) => {
const apiKey = resolveCodeExecutionApiKey(codeExecutionConfig, options?.config);
if (!apiKey) {
return jsonResult({
error: "missing_xai_api_key",
message:
"code_execution needs an xAI API key. Set XAI_API_KEY in the Gateway environment, or configure tools.code_execution.apiKey or plugins.entries.xai.config.webSearch.apiKey.",
docs: "https://docs.openclaw.ai/tools/code-execution",
});
}
const task = readStringParam(args, "task", { required: true });
const codeExecutionConfigRecord = codeExecutionConfig as Record<string, unknown> | undefined;
const model = resolveXaiCodeExecutionModel(codeExecutionConfigRecord);
const maxTurns = resolveXaiCodeExecutionMaxTurns(codeExecutionConfigRecord);
const startedAt = Date.now();
const result = await requestXaiCodeExecution({
apiKey,
model,
timeoutSeconds: codeExecutionConfig?.timeoutSeconds ?? 30,
maxTurns,
task,
});
return jsonResult(
buildXaiCodeExecutionPayload({
task,
model,
tookMs: Date.now() - startedAt,
content: result.content,
citations: result.citations,
usedCodeExecution: result.usedCodeExecution,
outputTypes: result.outputTypes,
}),
);
},
};
}

View File

@ -1,3 +1,4 @@
export { createWebFetchTool, extractReadableContent, fetchFirecrawlContent } from "./web-fetch.js";
export { createWebSearchTool } from "./web-search.js";
export { createXSearchTool } from "./x-search.js";
export { createCodeExecutionTool } from "./code-execution.js";

View File

@ -10,6 +10,7 @@ describe("command secret target ids", () => {
const ids = getAgentRuntimeCommandSecretTargetIds();
expect(ids.has("agents.defaults.memorySearch.remote.apiKey")).toBe(true);
expect(ids.has("agents.list[].memorySearch.remote.apiKey")).toBe(true);
expect(ids.has("tools.code_execution.apiKey")).toBe(true);
expect(ids.has("tools.web.fetch.firecrawl.apiKey")).toBe(true);
expect(ids.has("tools.web.x_search.apiKey")).toBe(true);
});

View File

@ -23,6 +23,7 @@ const COMMAND_SECRET_TARGETS = {
"agents.list[].memorySearch.remote.",
"skills.entries.",
"messages.tts.",
"tools.code_execution",
"tools.web.search",
"tools.web.fetch.firecrawl.",
"tools.web.x_search",

View File

@ -7164,6 +7164,94 @@ export const GENERATED_BASE_CONFIG_SCHEMA = {
},
additionalProperties: false,
},
code_execution: {
type: "object",
properties: {
enabled: {
type: "boolean",
},
apiKey: {
anyOf: [
{
type: "string",
},
{
oneOf: [
{
type: "object",
properties: {
source: {
type: "string",
const: "env",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
pattern: "^[A-Z][A-Z0-9_]{0,127}$",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "file",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
{
type: "object",
properties: {
source: {
type: "string",
const: "exec",
},
provider: {
type: "string",
pattern: "^[a-z][a-z0-9_-]{0,63}$",
},
id: {
type: "string",
},
},
required: ["source", "provider", "id"],
additionalProperties: false,
},
],
},
],
},
model: {
type: "string",
},
maxTurns: {
type: "integer",
minimum: -9007199254740991,
maximum: 9007199254740991,
},
timeoutSeconds: {
type: "integer",
exclusiveMinimum: 0,
maximum: 9007199254740991,
},
},
additionalProperties: false,
},
exec: {
type: "object",
properties: {
@ -12975,6 +13063,32 @@ export const GENERATED_BASE_CONFIG_SCHEMA = {
help: "Cache TTL in minutes for x_search results.",
tags: ["performance", "storage", "tools"],
},
"tools.code_execution.enabled": {
label: "Enable Code Execution Tool",
help: "Enable the code_execution tool (requires XAI_API_KEY or tools.code_execution.apiKey).",
tags: ["tools"],
},
"tools.code_execution.apiKey": {
label: "xAI API Key",
help: "xAI API key for remote code execution (fallback: XAI_API_KEY env var).",
tags: ["security", "auth", "tools"],
sensitive: true,
},
"tools.code_execution.model": {
label: "Code Execution Model",
help: 'Model to use for remote code execution (default: "grok-4-1-fast").',
tags: ["models", "tools"],
},
"tools.code_execution.maxTurns": {
label: "Code Execution Max Turns",
help: "Optional max internal tool turns xAI may use per code_execution request. Omit to let xAI choose.",
tags: ["performance", "tools"],
},
"tools.code_execution.timeoutSeconds": {
label: "Code Execution Timeout (sec)",
help: "Timeout in seconds for code_execution requests.",
tags: ["performance", "tools"],
},
"gateway.controlUi.basePath": {
label: "Control UI Base Path",
help: "Optional URL prefix where the Control UI is served (e.g. /openclaw).",

View File

@ -724,6 +724,15 @@ export const FIELD_HELP: Record<string, string> = {
"Optional max internal search/tool turns xAI may use per x_search request. Omit to let xAI choose.",
"tools.web.x_search.timeoutSeconds": "Timeout in seconds for x_search requests.",
"tools.web.x_search.cacheTtlMinutes": "Cache TTL in minutes for x_search results.",
"tools.code_execution.enabled":
"Enable the code_execution tool (requires XAI_API_KEY or tools.code_execution.apiKey).",
"tools.code_execution.apiKey":
"xAI API key for remote code execution (fallback: XAI_API_KEY env var).",
"tools.code_execution.model":
'Model to use for remote code execution (default: "grok-4-1-fast").',
"tools.code_execution.maxTurns":
"Optional max internal tool turns xAI may use per code_execution request. Omit to let xAI choose.",
"tools.code_execution.timeoutSeconds": "Timeout in seconds for code_execution requests.",
models:
"Model catalog root for provider definitions, merge/replace behavior, and optional Bedrock discovery integration. Keep provider definitions explicit and validated before relying on production failover paths.",
"models.mode":

View File

@ -254,6 +254,11 @@ export const FIELD_LABELS: Record<string, string> = {
"tools.web.x_search.maxTurns": "X Search Max Turns",
"tools.web.x_search.timeoutSeconds": "X Search Timeout (sec)",
"tools.web.x_search.cacheTtlMinutes": "X Search Cache TTL (min)",
"tools.code_execution.enabled": "Enable Code Execution Tool",
"tools.code_execution.apiKey": "xAI API Key", // pragma: allowlist secret
"tools.code_execution.model": "Code Execution Model",
"tools.code_execution.maxTurns": "Code Execution Max Turns",
"tools.code_execution.timeoutSeconds": "Code Execution Timeout (sec)",
"gateway.controlUi.basePath": "Control UI Base Path",
"gateway.controlUi.root": "Control UI Assets Root",
"gateway.controlUi.allowedOrigins": "Control UI Allowed Origins",

View File

@ -476,6 +476,19 @@ type XSearchToolConfig = {
cacheTtlMinutes?: number;
};
type CodeExecutionToolConfig = {
/** Enable remote xAI code execution (default: true when an xAI API key is available). */
enabled?: boolean;
/** API key for xAI (defaults to XAI_API_KEY env var). Supports SecretRef. */
apiKey?: SecretInput;
/** Model id to use for remote code execution. */
model?: string;
/** Optional max internal tool turns for xAI to use. */
maxTurns?: number;
/** Timeout in seconds for code execution requests. */
timeoutSeconds?: number;
};
export type ToolsConfig = {
/** Base tool profile applied before allow/deny lists. */
profile?: ToolProfileId;
@ -485,6 +498,8 @@ export type ToolsConfig = {
deny?: string[];
/** Optional tool policy overrides keyed by provider id or "provider/model". */
byProvider?: Record<string, ToolPolicyConfig>;
/** Remote xAI sandboxed code execution. */
code_execution?: CodeExecutionToolConfig;
web?: {
search?: {
/** Enable web search tool (default: true when API key is present). */

View File

@ -361,6 +361,17 @@ export const ToolsWebXSearchSchema = z
.strict()
.optional();
export const ToolCodeExecutionSchema = z
.object({
enabled: z.boolean().optional(),
apiKey: SecretInputSchema.optional().register(sensitive),
model: z.string().optional(),
maxTurns: z.number().int().optional(),
timeoutSeconds: z.number().int().positive().optional(),
})
.strict()
.optional();
export const ToolsWebSchema = z
.object({
search: ToolsWebSearchSchema,
@ -854,6 +865,7 @@ export const ToolsSchema = z
})
.strict()
.optional(),
code_execution: ToolCodeExecutionSchema,
exec: ToolExecSchema,
fs: ToolFsSchema,
subagents: z

View File

@ -292,6 +292,33 @@ function collectMessagesTtsAssignments(params: {
});
}
function collectCodeExecutionAssignments(params: {
config: OpenClawConfig;
defaults: SecretDefaults | undefined;
context: ResolverContext;
}): void {
const tools = params.config.tools as Record<string, unknown> | undefined;
if (!isRecord(tools)) {
return;
}
const codeExecution = isRecord(tools.code_execution) ? tools.code_execution : undefined;
if (!codeExecution) {
return;
}
collectSecretInputAssignment({
value: codeExecution.apiKey,
path: "tools.code_execution.apiKey",
expected: "string",
defaults: params.defaults,
context: params.context,
active: codeExecution.enabled !== false,
inactiveReason: "tools.code_execution is disabled.",
apply: (value) => {
codeExecution.apiKey = value;
},
});
}
function collectCronAssignments(params: {
config: OpenClawConfig;
defaults: SecretDefaults | undefined;
@ -425,5 +452,6 @@ export function collectCoreConfigAssignments(params: {
collectGatewayAssignments(params);
collectSandboxSshAssignments(params);
collectMessagesTtsAssignments(params);
collectCodeExecutionAssignments(params);
collectCronAssignments(params);
}

View File

@ -208,6 +208,9 @@ function buildConfigForOpenClawTarget(entry: SecretRegistryEntry, envId: string)
if (entry.id === "plugins.entries.tavily.config.webSearch.apiKey") {
setPathCreateStrict(config, ["tools", "web", "search", "provider"], "tavily");
}
if (entry.id === "tools.code_execution.apiKey") {
setPathCreateStrict(config, ["tools", "code_execution", "enabled"], true);
}
if (entry.id === "tools.web.x_search.apiKey") {
setPathCreateStrict(config, ["tools", "web", "x_search", "enabled"], true);
}

View File

@ -703,6 +703,17 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
includeInConfigure: true,
includeInAudit: true,
},
{
id: "tools.code_execution.apiKey",
targetType: "tools.code_execution.apiKey",
configFile: "openclaw.json",
pathPattern: "tools.code_execution.apiKey",
secretShape: SECRET_INPUT_SHAPE,
expectedResolvedValue: "string",
includeInPlan: true,
includeInConfigure: true,
includeInAudit: true,
},
{
id: "tools.web.fetch.firecrawl.apiKey",
targetType: "tools.web.fetch.firecrawl.apiKey",