Skip to content

Cloud Tasks

源码版本rust-v0.145.0

codex cloud es el subcomando que envía el prompt a la nube de OpenAI para correr: localmente no llama al modelo ni corre el bucle del Agent; solo se encarga de enviar la tarea, recoger el estado, sacar el diff y aplicar el parche. cloud-tasks es la entrada de TUI + CLI; cloud-tasks-client ofrece el trait CloudBackend + la implementación HTTP; cloud-tasks-mock-client inyecta un backend falso para debug; backend-client es el HTTP subyacente (reutilizado por otras backend APIs). A diferencia de la inferencia local de Model Provider, aquí «la inferencia del modelo» ocurre dentro de un contenedor en la nube.

Responsabilidades

  1. Enviar la tarea: run_exec_command empaqueta prompt + git ref + environment id y hace POST /wham/tasks (o /api/codex/tasks), devolviendo el task id (codex-rs/cloud-tasks/src/lib.rs:161-184).
  2. Listado / detalle / diff: run_list_command, run_status_command, run_diff_command son tres subcomandos CLI que se mapean a list_tasks / get_task_summary / get_task_diff (codex-rs/cloud-tasks/src/cli.rs:16-27).
  3. Aplicar el parche: apply_task hace primero un preflight dry-run y luego un apply real; diff_override permite al usuario elegir un intento concreto dentro de un best-of-N (codex-rs/cloud-tasks-client/src/http.rs:99-121).
  4. Abstraer el trait: CloudBackend reúne todos los métodos de interacción con el backend como un trait; el build de debug usa MockClient y el de release usa HttpClient (codex-rs/cloud-tasks-client/src/api.rs:136-176).
  5. Inicializar el backend: init_backend ensambla base URL, UA, auth provider y ChatGPT-Account-Id, y cambia entre el estilo de ruta WHAM / Codex API según CODEX_CLOUD_TASKS_BASE_URL (codex-rs/cloud-tasks/src/lib.rs:43-107).

Motivación de diseño

Es un crate aparte y no está dentro de core, porque esta ruta no corre el bucle principal del Agent. El client de core envía peticiones de stream al modelo, parsea SSE y mantiene turn state; cloud task es «envía una tarea larga a la nube y espera el resultado asíncronamente». Los dos modelos de concurrencia son completamente distintos: el primero es una conexión larga de stream; el segundo, poll + pull. El trait CloudBackend existe para que el TUI pueda correr en builds de debug: cuando init_backend detecta CODEX_CLOUD_TASKS_MODE=mock, cambia directamente a MockClient, permitiendo depurar la máquina de estados de la UI sin conexión real al backend de ChatGPT. El HttpClient reutiliza por dentro backend-client::Client en vez de levantar otro HTTP aparte, porque las cabeceras de auth, la cookie store de Cloudflare y el estilo de ruta (wham vs codex-api) son consistentes con otras llamadas a la backend API.

Best-of-N es la particularidad de esta ruta: create_task acepta attempts: usize (1-4); la nube corre varios intentos; list_sibling_attempts los recupera a todos; ApplyCommand deja al usuario especificar --attempt N para elegir un resultado. El campo diff_override atraviesa apply_task_preflight y apply_task, de modo que en la fase de apply no se vuelve a tirar del diff, sino que se usa el elegido por el usuario.

Archivos clave

codex-rs/cloud-tasks/src/lib.rs:735-744run_main, dispatch de subcomandos + entrada al modo TUI.codex-rs/cloud-tasks/src/lib.rs:43-107BackendContext e init_backend, elige mock / http por variable de entorno y ensambla auth.codex-rs/cloud-tasks/src/cli.rs:29-50ExecCommand, define los tres parámetros centrales: --env, --attempts, --branch.codex-rs/cloud-tasks-client/src/api.rs:136-176 — trait CloudBackend, 11 métodos que cubren list / get / apply / create.codex-rs/cloud-tasks-client/src/http.rs:25-63HttpClient, envuelve backend-client::Client e implementa CloudBackend.codex-rs/backend-client/src/client.rs:451-482create_task en el base client, saca el task id del JSON.codex-rs/cloud-tasks-mock-client/src/mock.rs:163-189MockClient implementa el trait y delega a las funciones internas de generación de datos mock.

La factory init_backend comprueba variables de entorno en build de debug y decide entre mock o HTTP real:

rust
// cloud-tasks/src/lib.rs:43-60 — selección de backend
async fn init_backend(user_agent_suffix: &str) -> anyhow::Result<BackendContext> {
    #[cfg(debug_assertions)]
    let use_mock = matches!(
        std::env::var("CODEX_CLOUD_TASKS_MODE").ok().as_deref(),
        Some("mock") | Some("MOCK")
    );
    let base_url = std::env::var("CODEX_CLOUD_TASKS_BASE_URL")
        .unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string());
    #[cfg(debug_assertions)]
    if use_mock {
        return Ok(BackendContext {
            backend: Arc::new(codex_cloud_tasks_mock_client::MockClient),
            base_url,
        });
    }

El trait CloudBackend usa CloudBackendFuture para unificar el tipo de retorno como boxed future, de modo que mock y http compartan la misma firma:

rust
// cloud-tasks-client/src/api.rs:136-176 — abstracción de backend
pub trait CloudBackend: Send + Sync {
    fn list_tasks<'a>(
        &'a self,
        env: Option<&'a str>,
        limit: Option<i64>,
        cursor: Option<&'a str>,
    ) -> CloudBackendFuture<'a, TaskListPage>;
    fn get_task_summary(&self, id: TaskId) -> CloudBackendFuture<'_, TaskSummary>;
    fn get_task_diff(&self, id: TaskId) -> CloudBackendFuture<'_, Option<String>>;
    // ...messages / sibling attempts / apply preflight / apply / create
    fn create_task<'a>(
        &'a self,
        env_id: &'a str,
        prompt: &'a str,
        git_ref: &'a str,
        qa_mode: bool,
        best_of_n: usize,
    ) -> CloudBackendFuture<'a, CreatedTask>;
}

create_task en el base client hace el POST y luego saca el task id del JSON (prioriza task.id, y si no, el id del nivel superior):

rust
// backend-client/src/client.rs:451-482 — POST /wham/tasks y extracción del task id
pub async fn create_task(&self, request_body: serde_json::Value) -> Result<String> {
    let url = match self.path_style {
        PathStyle::CodexApi => format!("{}/api/codex/tasks", self.base_url),
        PathStyle::ChatGptApi => format!("{}/wham/tasks", self.base_url),
    };
    // ...envía la petición, parsea el body
    match serde_json::from_str::<serde_json::Value>(&body) {
        Ok(v) => {
            if let Some(id) = v.get("task").and_then(|t| t.get("id")).and_then(|s| s.as_str()) {
                Ok(id.to_string())
            } else if let Some(id) = v.get("id").and_then(|s| s.as_str()) {
                Ok(id.to_string())
            } else {
                anyhow::bail!("POST {url} succeeded but no task id found; ...");
            }
        }
        Err(e) => anyhow::bail!("Decode error for {url}: {e}; ..."),
    }
}

Flujo de datos

Bordes y fallos

Resumen

Cloud Tasks comprime los tres pasos «enviar, consultar, aplicar» en el trait CloudBackend; HTTP y mock implementan el mismo trait, permitiendo al TUI depurar sin backend local. El HTTP subyacente reutiliza backend-client::Client, compartiendo autenticación y estilo de ruta con Model Provider. La configuración propia de las tareas en la nube (environments permitidos, políticas enterprise) la provee el cloud config bundle de Sistema de configuración, que se descarga al arranque y se aplica.