theater 0.3.11

A WebAssembly actor system for AI agents
Documentation
//! # Actor Types
//!
//! This module defines the core data types and error types used throughout the actor system.
//! These include operation types, error definitions, and other shared types needed for
//! the actor system to function.

use crate::metrics::ActorMetrics;
use crate::pack_bridge::{InterfaceHash, Value};
use crate::ChainEvent;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use thiserror::Error;
use tokio::sync::oneshot;
use tokio::time::Duration;
use wasmtime::component::{ComponentType, Lift, Lower};

/// Default timeout for actor operations (50 minutes)
pub const DEFAULT_OPERATION_TIMEOUT: Duration = Duration::from_secs(3000);

/// # ActorError
///
/// Represents errors that can occur during actor execution.
///
/// This enum provides detailed error information for various failure modes that
/// might occur when interacting with an actor. These errors are propagated back
/// to callers to help diagnose and handle problems.
#[derive(
    Error, Debug, Clone, ComponentType, Lift, Lower, Serialize, Deserialize, PartialEq, Hash, Eq,
)]
#[component(variant)]
pub enum ActorError {
    /// Operation exceeded the maximum allowed execution time
    #[error("Operation timed out after {0:?}")]
    #[component(name = "operation-timeout")]
    OperationTimeout(u64),

    /// Communication channel to the actor was closed unexpectedly
    #[error("Operation channel closed")]
    #[component(name = "channel-closed")]
    ChannelClosed,

    /// Actor is in the process of shutting down and cannot accept new operations
    #[error("Actor is shutting down")]
    #[component(name = "shutting-down")]
    ShuttingDown,

    /// The requested WebAssembly function was not found in the actor
    #[error("Function not found: {0}")]
    #[component(name = "function-not-found")]
    FunctionNotFound(String),

    /// Parameter or return types did not match the WebAssembly function signature
    #[error("Type mismatch for function {0}")]
    #[component(name = "type-mismatch")]
    TypeMismatch(String),

    /// An internal error occurred during execution
    #[error("Internal error: {0}")]
    #[component(name = "internal-error")]
    Internal(ChainEvent),

    /// Unexpected error
    #[error("Unexpected error: {0}")]
    #[component(name = "unexpected-error")]
    UnexpectedError(String),

    /// Failed to serialize or deserialize data
    #[error("Serialization error")]
    #[component(name = "serialization-error")]
    SerializationError,

    /// Failed to update the actor's WebAssembly package
    #[error("Failed to update package: {0}")]
    #[component(name = "update-package-error")]
    UpdatePackageError(String),

    /// Actor is paused
    #[error("Actor is paused")]
    #[component(name = "actor-paused")]
    Paused,

    /// Actor is not paused
    #[error("Actor is not paused")]
    #[component(name = "actor-not-paused")]
    NotPaused,
    // Failed to update actor due to permission or validation error
    //#[error("Actor update failed: {0}")]
    //#[component(name = "update-error")]
    //UpdateError(String),
    #[error("Handler error: {0}")]
    #[component(name = "handler-error")]
    HandlerError(String),
}

#[derive(Debug, Clone, ComponentType, Lift, Lower, Serialize, Deserialize)]
#[component(record)]
pub struct WitActorError {
    #[component(name = "error-type")]
    error_type: WitErrorType,
    data: Option<Vec<u8>>,
}

#[derive(Debug, Clone, ComponentType, Lift, Lower, Serialize, Deserialize, Copy)]
#[component(enum)]
#[repr(u8)]
pub enum WitErrorType {
    #[component(name = "operation-timeout")]
    OperationTimeout,
    #[component(name = "channel-closed")]
    ChannelClosed,
    #[component(name = "shutting-down")]
    ShuttingDown,
    #[component(name = "function-not-found")]
    FunctionNotFound,
    #[component(name = "type-mismatch")]
    TypeMismatch,
    #[component(name = "internal")]
    Internal,
    #[component(name = "serialization-error")]
    SerializationError,
    #[component(name = "update-package-error")]
    UpdatePackageError,
    #[component(name = "paused")]
    Paused,
    //#[component(name = "update-error")]
    //UpdateError,
}

impl From<ActorError> for WitActorError {
    fn from(error: ActorError) -> Self {
        let (error_type, data) = match error {
            ActorError::OperationTimeout(data) => (
                WitErrorType::OperationTimeout,
                Some(data.to_le_bytes().to_vec()),
            ),
            ActorError::ChannelClosed => (WitErrorType::ChannelClosed, None),
            ActorError::ShuttingDown => (WitErrorType::ShuttingDown, None),
            ActorError::FunctionNotFound(data) => {
                (WitErrorType::FunctionNotFound, Some(data.into_bytes()))
            }
            ActorError::TypeMismatch(data) => (WitErrorType::TypeMismatch, Some(data.into_bytes())),
            ActorError::Internal(data) => (
                WitErrorType::Internal,
                Some(serde_json::to_vec(&data).unwrap()),
            ),
            ActorError::SerializationError => (WitErrorType::SerializationError, None),
            ActorError::UpdatePackageError(data) => {
                (WitErrorType::UpdatePackageError, Some(data.into_bytes()))
            }
            ActorError::Paused => (WitErrorType::Paused, None),
            ActorError::NotPaused => (WitErrorType::Paused, None),
            ActorError::UnexpectedError(data) => (WitErrorType::Internal, Some(data.into_bytes())),
            ActorError::HandlerError(data) => (WitErrorType::Internal, Some(data.into_bytes())),
            //        ActorError::UpdateError(data) => (WitErrorType::UpdateError, Some(data.into_bytes())),
        };
        Self { error_type, data }
    }
}

/// # ActorOperation
///
/// Represents the different types of operations that can be performed on an actor.
///
/// This enum defines the message types that can be sent to an `ActorRuntime` via
/// its operation channel. Each variant includes the necessary data for the operation
/// and a oneshot channel sender for returning the result.
#[derive(Debug)]
pub enum ActorOperation {
    /// Call a WebAssembly function using Pack-native Value encoding.
    ///
    /// Params and result are Pack ABI encoded bytes (encode_value/decode_value).
    /// Preserves structured type information through the call boundary.
    CallFunctionPack {
        /// Name of the function to call
        name: String,
        /// Pack ABI encoded params (encode_value(Value) bytes)
        params: Vec<u8>,
        /// Channel to send the result back to the caller
        response_tx: oneshot::Sender<Result<Vec<u8>, ActorError>>,
    },
    /// Handle a WASI HTTP incoming request
    /// This operation creates resources in the actor's store and calls the exported
    /// wasi:http/incoming-handler.handle function
    HandleWasiHttpRequest {
        /// HTTP method (GET, POST, etc.)
        method: String,
        /// URL scheme (http, https, etc.)
        scheme: Option<String>,
        /// Authority (host:port)
        authority: Option<String>,
        /// Path with query string
        path_with_query: Option<String>,
        /// Request headers as (name, value) pairs
        headers: Vec<(String, Vec<u8>)>,
        /// Request body
        body: Vec<u8>,
        /// Channel to send the response back
        response_tx: oneshot::Sender<Result<WasiHttpResponse, ActorError>>,
    },
}

/// Response from a WASI HTTP request
#[derive(Debug, Clone)]
pub struct WasiHttpResponse {
    /// HTTP status code
    pub status: u16,
    /// Response headers
    pub headers: Vec<(String, Vec<u8>)>,
    /// Response body
    pub body: Vec<u8>,
}

#[derive(Debug)]
pub enum ActorControl {
    /// Pause the actor
    Pause {
        /// Channel to confirm pause completion
        response_tx: oneshot::Sender<Result<(), ActorError>>,
    },
    /// Resume the actor
    Resume {
        /// Channel to confirm resume completion
        response_tx: oneshot::Sender<Result<(), ActorError>>,
    },
    /// Initiate actor shutdown
    Shutdown {
        /// Channel to confirm shutdown completion
        response_tx: oneshot::Sender<Result<(), ActorError>>,
    },
    /// Forcefully terminate the actor, aborting any ongoing operations
    Terminate {
        /// Channel to confirm termination completion
        response_tx: oneshot::Sender<Result<(), ActorError>>,
    },
}

#[derive(Debug)]
pub enum ActorInfo {
    /// Retrieve the actor's current state
    GetState {
        /// Channel to send state back to the caller
        response_tx: oneshot::Sender<Result<Value, ActorError>>,
    },
    /// Retrieve the actor's event chain (audit log)
    GetChain {
        /// Channel to send chain events back to the caller
        response_tx: oneshot::Sender<Result<Vec<ChainEvent>, ActorError>>,
    },
    SaveChain {
        response_tx: oneshot::Sender<Result<(), ActorError>>,
    },
    /// Retrieve performance metrics for the actor
    GetMetrics {
        /// Channel to send metrics back to the caller
        response_tx: oneshot::Sender<Result<ActorMetrics, ActorError>>,
    },
    /// Retrieve the actor's current processing state
    GetStatus {
        /// Channel to send processing state back to the caller
        response_tx: oneshot::Sender<Result<String, ActorError>>,
    },
    /// Retrieve the actor's exported interface hashes
    GetExportHashes {
        /// Channel to send export hashes back to the caller
        response_tx: oneshot::Sender<Result<Vec<InterfaceHash>, ActorError>>,
    },
}