For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navegación principal

Shell local

Permite que los agentes ejecuten comandos en un shell local.

La herramienta de shell local está desactualizada. Para nuevos casos de uso, usa la herramienta shell con GPT-5.1 en su lugar. Más información.

Shell local es una herramienta que permite a los agentes ejecutar comandos de shell localmente en una máquina proporcionada por ti o por el usuario. Está diseñada para funcionar con Codex CLI y codex-mini-latest. Los comandos se ejecutan dentro de tu propio entorno de ejecución, por lo que tienes el control total sobre qué comandos se ejecutan realmente. La API solo devuelve instrucciones; no las ejecuta en la infraestructura de OpenAI.

Shell local está disponible a través de la API Responses para usarlo con codex-mini-latest. No está disponible con otros modelos ni a través de la API para completar chats.

Ejecutar comandos de shell arbitrarios puede ser peligroso. Aísla siempre la ejecución en un sandbox o agrega listas estrictas de comandos permitidos o bloqueados antes de enviar un comando al shell del sistema.


Consulta Codex CLI para ver una implementación de referencia.

Cómo funciona

La herramienta de shell local permite que los agentes funcionen en un bucle continuo con acceso a una terminal.

El modelo envía comandos de shell que tu código ejecuta en una máquina local antes de devolverle la salida al modelo. Este bucle permite que el modelo complete el ciclo de compilación, pruebas y ejecución sin intervención adicional del usuario.

Tu código debe implementar un bucle que espere elementos de salida local_shell_call y ejecute los comandos que contienen. Recomendamos enfáticamente aislar la ejecución en un sandbox para evitar que se ejecuten comandos inesperados.

Integración de la herramienta de shell local

Estos son los pasos generales que debes seguir para integrar la herramienta de shell local en tu aplicación:

  1. Envía una solicitud al modelo: incluye la herramienta local_shell entre las herramientas disponibles.

  2. Recibe una respuesta del modelo: verifica si la respuesta contiene elementos local_shell_call. Esta llamada a la herramienta contiene una acción como exec con un comando para ejecutar.

  3. Ejecuta la acción solicitada: ejecuta el comando en el entorno local que controlas.

  4. Devuelve la salida de la acción: después de ejecutar la acción, devuelve la salida del comando al modelo.

  5. Repite: envía una nueva solicitud con el estado actualizado como un local_shell_call_output y repite este bucle hasta que el modelo deje de solicitar acciones o decidas detenerlo.

Ejemplo de flujo de trabajo

A continuación se muestra un ejemplo mínimo del bucle de solicitud y respuesta. Elige un lenguaje para ver el flujo de trabajo equivalente con su SDK. Por brevedad, se omiten el aislamiento en un sandbox y las comprobaciones de seguridad necesarios para producción. No ejecutes comandos que no sean de confianza en producción sin medidas de protección adicionales.

import { spawn } from "node:child_process";
import process from "node:process";
import OpenAI from "openai";

const client = new OpenAI();
const MAX_TIMEOUT_MS = 10_000;

function runCommand(command, options) {
  return new Promise((resolve) => {
    let stdout = "";
    let stderr = "";
    let settled = false;
    let groupPoll;
    const child = spawn(command[0], command.slice(1), {
      ...options,
      detached: process.platform !== "win32",
      stdio: ["ignore", "pipe", "pipe"],
    });
    const finish = (suffix = "") => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      clearTimeout(groupPoll);
      resolve(stdout + stderr + suffix);
    };
    const processGroupIsRunning = () => {
      if (process.platform === "win32" || !child.pid) return false;
      try {
        process.kill(-child.pid, 0);
        return true;
      } catch {
        return false;
      }
    };
    const finishAfterProcessGroup = (suffix) => {
      if (settled) return;
      if (processGroupIsRunning()) {
        groupPoll = setTimeout(() => finishAfterProcessGroup(suffix), 10);
      } else {
        finish(suffix);
      }
    };
    const killProcessTree = () => {
      try {
        if (process.platform !== "win32" && child.pid) {
          process.kill(-child.pid, "SIGKILL");
        } else {
          child.kill("SIGKILL");
        }
      } catch {
        child.kill("SIGKILL");
      }
      child.stdout?.destroy();
      child.stderr?.destroy();
    };
    const timer = setTimeout(() => {
      killProcessTree();
      finish("Command timed out.\n");
    }, options.timeout);

    child.stdout?.on("data", (chunk) => {
      stdout += chunk;
    });
    child.stderr?.on("data", (chunk) => {
      stderr += chunk;
    });
    child.on("error", (error) => {
      finish(`Command failed: ${error.message}.\n`);
    });
    child.on("close", (code, signal) => {
      if (signal) {
        finishAfterProcessGroup(`Command failed with signal ${signal}.\n`);
      } else if (code !== 0) {
        finishAfterProcessGroup(`Command failed with exit code ${code}.\n`);
      } else {
        finishAfterProcessGroup("");
      }
    });
  });
}

let response = await client.responses.create({
  model: "codex-mini-latest",
  tools: [{ type: "local_shell" }],
  parallel_tool_calls: false,
  input: "List files in the current directory.",
});

while (true) {
  const shellCall = response.output.find(
    (item) => item.type === "local_shell_call"
  );
  if (!shellCall) break;

  const { command, env, timeout_ms, user, working_directory } =
    shellCall.action;
  let output;
  if (user) {
    output = `Unsupported execution user: ${user}.\n`;
  } else if (command.length === 0) {
    output = "Command is empty.\n";
  } else {
    const timeout =
      timeout_ms && timeout_ms > 0
        ? Math.min(timeout_ms, MAX_TIMEOUT_MS)
        : MAX_TIMEOUT_MS;
    try {
      output = await runCommand(command, {
        cwd: working_directory ?? process.cwd(),
        env: { PATH: process.env.PATH ?? "", ...env },
        timeout,
      });
    } catch (error) {
      output = `Command failed: ${error instanceof Error ? error.message : String(error)}.\n`;
    }
  }

  response = await client.responses.create({
    model: "codex-mini-latest",
    tools: [{ type: "local_shell" }],
    parallel_tool_calls: false,
    previous_response_id: response.id,
    input: [
      {
        type: "local_shell_call_output",
        id: shellCall.call_id,
        output,
      },
    ],
  });
}

console.log(response.output_text);

Prácticas recomendadas

  • Aísla en un sandbox o un contenedor la ejecución. Considera usar Docker o una cuenta de usuario confinada.
  • Impón límites de recursos (tiempo, memoria, red). El valor de timeout_ms que proporciona el modelo es solo una sugerencia; debes aplicar tus propios límites.
  • Filtra o examina detenidamente los comandos de alto riesgo (por ejemplo, rm, curl y las utilidades de red).
  • Registra cada comando y su salida para realizar auditorías y depurar errores.

Manejo de errores

Si el comando falla en tu entorno, por ejemplo, con un código de salida distinto de cero o por agotarse el tiempo de espera, puedes enviar un local_shell_call_output de todos modos; incluye el mensaje de error en el campo output.

El modelo puede optar por recuperarse del error o intentar ejecutar otro comando. Si envías datos con un formato incorrecto (por ejemplo, si falta id), la API devuelve un error de validación estándar 400.