af-agent-runtime 0.3.0

Recoverable Turn/Step loop, tool pipeline, retry and context compaction.
Documentation
use af_llm::{FinishReason, LlmError};

/// Failure while running a Turn.
#[derive(Debug, thiserror::Error)]
pub enum RuntimeError {
    /// Invalid input.
    #[error("invalid input: {0}")]
    InvalidInput(String),
    /// Session already has an active run.
    #[error("session already has an active run")]
    SessionBusy,
    /// Session invariant failed.
    #[error("session invariant failed: {0}")]
    Invariant(String),
    /// Model failed.
    #[error("model failed: {0}")]
    Model(String),
    /// Run cancelled.
    #[error("run cancelled")]
    Cancelled,
    /// Model attempt interrupted by steering input.
    #[error("model attempt interrupted by steering input")]
    Steered,
    /// Model returned no choice.
    #[error("model returned no choice")]
    EmptyModelResponse,
    /// Model stopped with finish reason.
    #[error("model stopped with finish reason: {0}")]
    FinishReason(FinishReason),
    /// Invalid arguments for tool.
    #[error("invalid arguments for tool {0}: {1}")]
    InvalidToolArguments(String, String),
    /// Tool denied.
    #[error("tool {0} denied: {1}")]
    ToolDenied(String, String),
    /// An extension hook failed.
    #[error("plugin hook failed: {0}")]
    Hook(String),
    /// Extension deadline exceeded.
    #[error("extension deadline exceeded: {0}")]
    ExtensionDeadline(String),
    /// Context contribution failed.
    #[error("context contribution failed: {0}")]
    Context(String),
    /// Conversation changed while compaction was running.
    #[error("conversation changed while compaction was running")]
    CompactionConflict,
    /// Event append failed.
    #[error("event append failed: {0}")]
    Event(String),
}

impl RuntimeError {
    /// Stable machine-readable code persisted as `error_code`.
    pub fn code(&self) -> &'static str {
        match self {
            Self::InvalidInput(_) => "invalid_input",
            Self::SessionBusy => "session_busy",
            Self::CompactionConflict => "compaction_conflict",
            Self::Cancelled => "cancelled",
            Self::Steered => "steered",
            Self::ToolDenied(_, _) => "tool_denied",
            _ => "agent_run_failed",
        }
    }
}

impl From<LlmError> for RuntimeError {
    fn from(error: LlmError) -> Self {
        Self::Model(error.to_string())
    }
}