use meerkat_core::SessionId;
use meerkat_core::SurfaceSessionRecoveryError;
use meerkat_core::service::SessionError;
#[cfg(all(feature = "live", not(target_arch = "wasm32")))]
use super::live_orchestration::RealtimeSessionOpenProjectionError;
#[derive(Debug, thiserror::Error)]
pub enum LiveOpenPrecheckError {
#[error("failed to resolve live session {session_id}: {source}")]
SessionLookup {
session_id: SessionId,
#[source]
source: SessionError,
},
#[error("model {model} (provider {provider}) does not support realtime")]
ModelNotRealtime {
model: String,
provider: &'static str,
},
#[error("provider {provider} has no live adapter wired in this build")]
ProviderHasNoLiveAdapter {
provider: &'static str,
},
}
#[derive(Debug, thiserror::Error)]
pub enum RecoveryError {
#[error(transparent)]
Recovery(#[from] SurfaceSessionRecoveryError),
#[error("failed to prepare runtime bindings for session {session_id}: {message}")]
BindingPreparation {
session_id: SessionId,
message: String,
},
#[error(transparent)]
Session(#[from] SessionError),
}
impl RecoveryError {
#[must_use]
pub const fn code(&self) -> &'static str {
match self {
Self::Recovery(SurfaceSessionRecoveryError::InvalidOverride(_)) => "INVALID_OVERRIDE",
Self::Recovery(SurfaceSessionRecoveryError::MissingSessionMetadata(_)) => {
"MISSING_SESSION_METADATA"
}
Self::Recovery(SurfaceSessionRecoveryError::MissingSessionBuildState(_)) => {
"MISSING_SESSION_BUILD_STATE"
}
Self::BindingPreparation { .. } => "BINDING_PREPARATION",
Self::Session(_) => "SESSION",
}
}
}
impl LiveOpenPrecheckError {
#[must_use]
pub const fn code(&self) -> &'static str {
match self {
Self::SessionLookup { .. } => "SESSION_LOOKUP",
Self::ModelNotRealtime { .. } => "MODEL_NOT_REALTIME",
Self::ProviderHasNoLiveAdapter { .. } => "PROVIDER_HAS_NO_LIVE_ADAPTER",
}
}
}
#[cfg(all(feature = "live", not(target_arch = "wasm32")))]
pub const LIVE_INGRESS_INVALID_SESSION_MESSAGE: &str =
"keep_alive requires a session created with comms_name";
#[cfg(all(feature = "live", not(target_arch = "wasm32")))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LiveIngressError {
InvalidSession,
Internal(String),
}
#[cfg(all(feature = "live", not(target_arch = "wasm32")))]
impl std::fmt::Display for LiveIngressError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidSession => f.write_str(LIVE_INGRESS_INVALID_SESSION_MESSAGE),
Self::Internal(message) => f.write_str(message),
}
}
}
#[cfg(all(feature = "live", not(target_arch = "wasm32")))]
impl std::error::Error for LiveIngressError {}
#[cfg(all(feature = "live", not(target_arch = "wasm32")))]
#[derive(Debug, thiserror::Error)]
pub enum LiveOpenError {
#[error("session {session_id} not found")]
SessionNotFound { session_id: SessionId },
#[error("live session presence read failed: {0}")]
SessionStateFault(#[source] SessionError),
#[error("live open requires a realtime session factory; none is wired in this build")]
RealtimeFactoryMissing,
#[error("failed to build session config: {0}")]
OpenConfig(#[source] RealtimeSessionOpenProjectionError),
#[error("live open authority rejected admission: {0}")]
AdmissionAuthority(#[source] meerkat_runtime::RuntimeDriverError),
#[error("session {session_id} already has an active live channel")]
AdmissionRejectedAlreadyBound { session_id: SessionId },
#[error("generated duplicate live channel id {channel_id}")]
AdmissionRejectedChannelCollision { channel_id: String },
#[error("session lifecycle is closed to live channel admission")]
AdmissionRejectedLifecycleClosed,
#[error("live open authority rejected admission without a reason")]
AdmissionRejectedNoReason,
#[error("live open admission was accepted without a generated host handoff")]
MissingHostHandoff,
#[error(
"live host transport cache still has active channel for session {session_id} after generated admission"
)]
HostOpenSessionAlreadyBound { session_id: SessionId },
#[error("{0}")]
HostOpen(#[source] meerkat_live::LiveAdapterHostError),
#[error(transparent)]
Precheck(LiveOpenPrecheckError),
#[error("provider {provider} has no live adapter wired in this build")]
ProviderUnsupportedByFactory { provider: &'static str },
#[error("failed to open provider session: {0}")]
AdapterOpen(#[source] meerkat_llm_core::LlmError),
#[error("failed to attach adapter: {0}")]
AdapterAttach(#[source] meerkat_live::LiveAdapterHostError),
#[error(transparent)]
Ingress(LiveIngressError),
#[error("live/open reached handler without configured live transport")]
NoTransportConfigured,
#[error("live transport websocket is not configured")]
WebsocketNotConfigured,
#[error("live WebSocket token authority rejected issue: {0}")]
TokenMint(String),
#[error(
"live websocket transport requires a resolved audio policy; no realtime factory audio format was available"
)]
AudioPolicyMissing,
#[error(
"resolved live audio policy (input {input_sample_rate_hz}Hz/{input_channels}ch) has no websocket binary format the transport can negotiate"
)]
AudioFormatUnmappable {
input_sample_rate_hz: u32,
input_channels: u16,
},
#[error("live transport webrtc is not configured")]
WebrtcNotConfigured,
#[error("live transport webrtc is not compiled into this build")]
WebrtcNotCompiled,
#[error("{0}")]
WebrtcClock(String),
#[error("live WebRTC token authority rejected issue: {0}")]
WebrtcTokenMint(String),
#[error("unsupported live transport")]
UnsupportedTransport,
}
#[cfg(all(feature = "live", not(target_arch = "wasm32")))]
#[derive(Debug, thiserror::Error)]
pub enum LiveChannelVerbError {
#[error("live channel {channel_id} is not bound to a session")]
UnboundCommand {
channel_id: String,
authority: meerkat_runtime::meerkat_machine::LiveCommandRejectionAuthority,
expected: meerkat_runtime::meerkat_machine::dsl::LiveCommandPublicKind,
},
#[error("live channel {channel_id} is not bound to a session")]
UnboundRequest {
channel_id: String,
authority: meerkat_runtime::meerkat_machine::LiveChannelRequestRejectionAuthority,
expected: meerkat_runtime::meerkat_machine::dsl::LiveChannelRequestPublicKind,
detail: Option<String>,
},
#[error("live channel {channel_id} is not bound to the addressed session")]
SessionPinMismatch { channel_id: String },
#[error("{message}")]
RejectionAuthorityFailed { message: String },
#[error("live command rejected for channel {channel_id}: {detail}")]
CommandRejected {
channel_id: String,
authority: meerkat_runtime::meerkat_machine::LiveCommandRejectionAuthority,
expected: meerkat_runtime::meerkat_machine::dsl::LiveCommandPublicKind,
detail: String,
host_error: Box<meerkat_live::LiveAdapterHostError>,
},
#[error("live channel request rejected for channel {channel_id}: {detail:?}")]
RequestRejected {
channel_id: String,
authority: meerkat_runtime::meerkat_machine::LiveChannelRequestRejectionAuthority,
expected: meerkat_runtime::meerkat_machine::dsl::LiveChannelRequestPublicKind,
detail: Option<String>,
},
#[error("{message}")]
ResultAuthority { message: String },
#[error("live close authority omitted host commit handoff")]
CommitOmitted,
#[error("live close host commit failed after generated authority: {message}")]
HostCommit { message: String },
#[error("{message}")]
ResultProjection { message: String },
#[error("failed to build session config: {0}")]
RefreshConfig(#[source] SessionError),
}