af-agent-runtime 0.2.0

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

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

impl RuntimeError {
    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())
    }
}