kcode-codex-runtime 0.1.0

Safe Codex CLI generation, web search, and model catalog runtime
Documentation
use std::{error::Error as StdError, fmt};

/// The result type returned by Codex operations.
pub type Result<T> = std::result::Result<T, Error>;

/// Stable failure classification for application adapters.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ErrorKind {
    /// The caller supplied an invalid request or configuration.
    InvalidInput,
    /// Codex or its sandbox launcher could not be started or completed.
    Unavailable,
    /// Codex authentication is missing or invalid.
    Authentication,
    /// The provider rejected the operation due to a usage or rate limit.
    RateLimited,
    /// The selected model is temporarily at capacity.
    Capacity,
    /// The operation exceeded its configured deadline.
    Timeout,
    /// The model input exceeded its supported limit.
    InputTooLarge,
    /// Codex completed without a usable assistant message.
    EmptyOutput,
    /// Codex returned output that did not satisfy its runtime protocol.
    Protocol,
}

/// A sanitized Codex configuration, process, provider, or protocol failure.
#[derive(Debug)]
pub struct Error {
    kind: ErrorKind,
    message: String,
}

impl Error {
    pub(crate) fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
        Self {
            kind,
            message: message.into(),
        }
    }

    /// Returns the stable failure classification.
    pub const fn kind(&self) -> ErrorKind {
        self.kind
    }

    /// Returns the sanitized failure detail.
    pub fn message(&self) -> &str {
        &self.message
    }
}

impl fmt::Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(&self.message)
    }
}

impl StdError for Error {}

pub(crate) fn runtime(error: impl fmt::Display) -> Error {
    Error::new(
        ErrorKind::Unavailable,
        clean_message(&error.to_string(), 500),
    )
}

pub(crate) fn clean_message(value: &str, limit: usize) -> String {
    let clean = value
        .chars()
        .map(|character| {
            if character.is_control() {
                ' '
            } else {
                character
            }
        })
        .take(limit)
        .collect::<String>()
        .split_whitespace()
        .collect::<Vec<_>>()
        .join(" ");
    if clean.is_empty() {
        "Codex operation failed".into()
    } else {
        clean
    }
}