codex-cli-sdk 0.0.1

Rust SDK for the OpenAI Codex CLI
Documentation
use serde_json::{Value, json};

/// Build a `thread.started` event JSON.
pub fn thread_started(thread_id: &str) -> Value {
    json!({ "type": "thread.started", "thread_id": thread_id })
}

/// Build a `turn.started` event JSON.
pub fn turn_started() -> Value {
    json!({ "type": "turn.started" })
}

/// Build a `turn.completed` event JSON with usage stats.
pub fn turn_completed(input: u64, cached: u64, output: u64) -> Value {
    json!({
        "type": "turn.completed",
        "usage": { "input_tokens": input, "cached_input_tokens": cached, "output_tokens": output }
    })
}

/// Build a `turn.failed` event JSON.
pub fn turn_failed(message: &str) -> Value {
    json!({ "type": "turn.failed", "error": { "message": message } })
}

// ── Item lifecycle builders ──────────────────────────────────

/// Build an `item.started` event wrapping an `agent_message` item.
pub fn agent_message_started(id: &str) -> Value {
    json!({ "type": "item.started", "item": { "type": "agent_message", "id": id, "text": "" } })
}

/// Build an `item.updated` event wrapping an `agent_message` delta.
pub fn agent_message_updated(id: &str, text: &str) -> Value {
    json!({ "type": "item.updated", "item": { "type": "agent_message", "id": id, "text": text } })
}

/// Build an `item.completed` event wrapping a final `agent_message`.
pub fn agent_message_completed(id: &str, text: &str) -> Value {
    json!({ "type": "item.completed", "item": { "type": "agent_message", "id": id, "text": text } })
}

/// Build an `item.started` event wrapping a `command_execution` item.
pub fn command_started(id: &str, command: &str) -> Value {
    json!({ "type": "item.started", "item": {
        "type": "command_execution", "id": id, "command": command,
        "aggregated_output": "", "status": "in_progress"
    }})
}

/// Build an `item.completed` event wrapping a finished `command_execution`.
pub fn command_completed(id: &str, command: &str, output: &str, exit_code: i32) -> Value {
    json!({ "type": "item.completed", "item": {
        "type": "command_execution", "id": id, "command": command,
        "aggregated_output": output, "exit_code": exit_code, "status": "completed"
    }})
}

/// Build a `reasoning` item started event.
pub fn reasoning_started(id: &str) -> Value {
    json!({ "type": "item.started", "item": { "type": "reasoning", "id": id, "text": "" } })
}

/// Build a `reasoning` item completed event.
pub fn reasoning_completed(id: &str, text: &str) -> Value {
    json!({ "type": "item.completed", "item": { "type": "reasoning", "id": id, "text": text } })
}

/// Build an `exec_approval_request` event JSON.
pub fn approval_request(id: &str, command: &str) -> Value {
    json!({ "type": "exec_approval_request", "id": id, "command": command })
}

/// Build an `error` event JSON.
pub fn error(message: &str) -> Value {
    json!({ "type": "error", "message": message })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::events::ThreadEvent;

    #[test]
    fn builders_produce_parseable_events() {
        let events = vec![
            thread_started("t1"),
            turn_started(),
            agent_message_started("msg-1"),
            agent_message_updated("msg-1", "hello"),
            agent_message_completed("msg-1", "hello world"),
            command_started("cmd-1", "ls"),
            command_completed("cmd-1", "ls", "file.txt", 0),
            reasoning_started("r-1"),
            reasoning_completed("r-1", "thinking..."),
            approval_request("ap-1", "rm -rf /"),
            turn_completed(100, 0, 50),
            turn_failed("oops"),
            error("bad"),
        ];

        for event_json in events {
            let result = serde_json::from_value::<ThreadEvent>(event_json.clone());
            assert!(
                result.is_ok(),
                "Failed to parse: {}",
                serde_json::to_string(&event_json).unwrap()
            );
        }
    }
}