megacode-agent-sdk 0.30.0

Drive a MEGA harness agent session from a Rust program.
Documentation
//! The one error type (spec slice-ar R8.6.1).

use std::fmt;

/// What went wrong with a session.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
    /// No harness binary could be found or obtained (R12).
    BinaryUnavailable(String),
    /// The session did not start: no `init` in time, or one this SDK cannot
    /// speak to (R6.2). The text names what arrived.
    Handshake(String),
    /// The session ended without being asked to.
    SessionEnded {
        /// What the backend could say about the end, when it has anything.
        diagnostics: Option<Diagnostics>,
    },
    /// A line from the session that is not JSON, or misses a field its type
    /// requires (R6.3).
    Protocol(String),
    /// The session refused a command, or the options it was started with:
    /// the protocol's error `code` and its `message`.
    Refused {
        /// The protocol's error code (`no_turn`, `bad_argument`, …).
        code: String,
        /// The session's own explanation.
        message: String,
    },
    /// A turn is still running: one turn at a time, until its `result`.
    TurnInProgress,
    /// The program left more than 10,000 events of a turn unread; the
    /// session was ended rather than stop reading (R8.5.5).
    SlowConsumer,
    /// The command does not fit the session's 8 MiB line limit (R8.5.2).
    TooLarge,
}

/// What is known about a session that ended unexpectedly. Every field is
/// optional: a backend fills what it has.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct Diagnostics {
    /// The exit code, when the session exited with one.
    pub exit_code: Option<i32>,
    /// The signal that ended the session, when one did.
    pub signal: Option<i32>,
    /// The last lines (at most 200) of the session's error output.
    pub stderr_tail: Vec<String>,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::BinaryUnavailable(why) => write!(f, "no harness binary: {why}"),
            Error::Handshake(why) => write!(f, "the session did not start: {why}"),
            Error::SessionEnded { diagnostics } => {
                f.write_str("the session ended unexpectedly")?;
                if let Some(d) = diagnostics {
                    if let Some(code) = d.exit_code {
                        write!(f, " (exit {code})")?;
                    }
                    if let Some(signal) = d.signal {
                        write!(f, " (signal {signal})")?;
                    }
                    if let Some(last) = d.stderr_tail.last() {
                        write!(f, ": {last}")?;
                    }
                }
                Ok(())
            }
            Error::Protocol(why) => write!(f, "protocol error: {why}"),
            Error::Refused { code, message } => write!(f, "refused ({code}): {message}"),
            Error::TurnInProgress => f.write_str("a turn is still running"),
            Error::SlowConsumer => f.write_str(
                "the session was ended: more than 10,000 events of a turn were left unread",
            ),
            Error::TooLarge => f.write_str("the command is over the session's 8 MiB line limit"),
        }
    }
}

impl std::error::Error for Error {}