use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
use crate::comms::{
PeerId, PeerLifecycleKind, PeerName, PeerRoute, SUPERVISOR_BRIDGE_INTENT, TrustedPeerDescriptor,
};
use crate::types::{ContentBlock, HandlingMode, RenderMetadata};
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct InteractionId(#[cfg_attr(feature = "schema", schemars(with = "String"))] pub Uuid);
impl std::fmt::Display for InteractionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResponseStatus {
Accepted,
Completed,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum TerminalityClass {
Progress,
Terminal { disposition: TerminalDisposition },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum TerminalDisposition {
Completed,
Failed,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum InteractionContent {
Message {
body: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
blocks: Option<Vec<ContentBlock>>,
},
Request {
intent: String,
params: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
blocks: Option<Vec<ContentBlock>>,
},
Response {
in_reply_to: InteractionId,
status: ResponseStatus,
result: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
blocks: Option<Vec<ContentBlock>>,
},
}
#[derive(Debug, Clone)]
pub struct InboxInteraction {
pub id: InteractionId,
pub from_route: Option<PeerId>,
pub from: String,
pub content: InteractionContent,
pub rendered_text: String,
pub handling_mode: HandlingMode,
pub render_metadata: Option<RenderMetadata>,
}
pub fn format_external_event_projection(source_name: &str, body: Option<&str>) -> String {
let label = format!("External event via {source_name}");
let body = body.map(str::trim).filter(|body| !body.is_empty());
match body {
Some(body) => format!("{label}: {body}"),
None => label,
}
}
pub fn format_peer_message_projection(from_peer: &str, body: &str) -> String {
format!("Peer message from {from_peer}:\n{body}")
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SendResponseCallProjection {
pub peer_id: PeerId,
pub display_name: Option<String>,
pub in_reply_to: String,
}
impl SendResponseCallProjection {
pub const TOOL_NAME: &'static str = "send_response";
pub const PEER_ID_FIELD: &'static str = "peer_id";
pub const DISPLAY_NAME_FIELD: &'static str = "display_name";
pub const IN_REPLY_TO_FIELD: &'static str = "in_reply_to";
pub const STATUS_FIELD: &'static str = "status";
pub const RESULT_FIELD: &'static str = "result";
pub fn new(
peer_id: PeerId,
display_name: Option<&str>,
in_reply_to: impl Into<String>,
) -> Self {
Self {
peer_id,
display_name: display_name
.map(str::trim)
.filter(|name| !name.is_empty())
.map(ToOwned::to_owned),
in_reply_to: in_reply_to.into(),
}
}
pub fn completed_example_args(&self) -> Value {
let mut args = serde_json::Map::new();
args.insert(
Self::PEER_ID_FIELD.to_string(),
Value::String(self.peer_id.to_string()),
);
if let Some(display_name) = &self.display_name {
args.insert(
Self::DISPLAY_NAME_FIELD.to_string(),
Value::String(display_name.clone()),
);
}
args.insert(
Self::IN_REPLY_TO_FIELD.to_string(),
Value::String(self.in_reply_to.clone()),
);
args.insert(
Self::STATUS_FIELD.to_string(),
Value::String("completed".to_string()),
);
Value::Object(args)
}
pub fn instruction_text(&self) -> String {
let args = serde_json::to_string(&self.completed_example_args())
.unwrap_or_else(|_| "{}".to_string());
format!(
"Reply with {} with arguments {args}. Use status=\"failed\" instead of \"completed\" when the request cannot be fulfilled, and include result only when the request contract provides a typed result payload.",
Self::TOOL_NAME
)
}
}
pub fn format_peer_request_projection(
from_peer_id: PeerId,
display_name: Option<&str>,
request_id: impl std::fmt::Display,
intent: &str,
params: &Value,
) -> String {
let params_str = if params.is_null() || matches!(params, Value::Object(map) if map.is_empty()) {
String::new()
} else {
format!(
"\nParams: {}",
serde_json::to_string_pretty(params).unwrap_or_default()
)
};
let request_id = request_id.to_string();
let display_suffix = display_name
.map(str::trim)
.filter(|name| !name.is_empty())
.map(|name| format!(" (display_name: {name})"))
.unwrap_or_default();
let response_call =
SendResponseCallProjection::new(from_peer_id, display_name, request_id.clone());
format!(
"Peer request from peer_id {from_peer_id}{display_suffix} (id: {request_id})\n\
Intent: {intent}{params_str}\n\
Request ID: {request_id}\n\
\n\
This is a correlated peer request. {} \
Do not answer this request with send_message.",
response_call.instruction_text()
)
}
pub fn format_peer_response_projection(
from_peer: &str,
in_reply_to: impl std::fmt::Display,
status: ResponseStatus,
result: &Value,
) -> String {
let status_str = match status {
ResponseStatus::Accepted => "accepted",
ResponseStatus::Completed => "completed",
ResponseStatus::Failed => "failed",
};
let result_str = if result.is_null() || matches!(result, Value::Object(map) if map.is_empty()) {
String::new()
} else {
format!(
"\nResult: {}",
serde_json::to_string_pretty(result).unwrap_or_default()
)
};
format!(
"Peer response from {from_peer} (to request: {in_reply_to})\n\
Status: {status_str}{result_str}"
)
}
pub fn format_peer_ack_projection(from_peer: &str, in_reply_to: impl std::fmt::Display) -> String {
format!("Peer ack from {from_peer} (to request: {in_reply_to})")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PeerInputClass {
ActionableMessage,
ActionableRequest,
ResponseProgress,
ResponseTerminal,
PeerLifecycleAdded,
PeerLifecycleRetired,
PeerLifecycleUnwired,
PeerLifecycleKickoffFailed,
PeerLifecycleKickoffCancelled,
SilentRequest,
Ack,
PlainEvent,
}
const fn peer_input_class_actionable_grouping(class: PeerInputClass) -> bool {
matches!(
class,
PeerInputClass::ActionableMessage
| PeerInputClass::ActionableRequest
| PeerInputClass::ResponseProgress
| PeerInputClass::ResponseTerminal
| PeerInputClass::PlainEvent
| PeerInputClass::PeerLifecycleKickoffFailed
| PeerInputClass::PeerLifecycleKickoffCancelled
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum PeerIngressAuthExemption {
SupervisorBridge,
}
impl PeerIngressAuthExemption {
pub const fn intent(self) -> &'static str {
match self {
Self::SupervisorBridge => SUPERVISOR_BRIDGE_INTENT,
}
}
pub fn matches_intent(self, intent: &str) -> bool {
self.intent() == intent
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PeerIngressAuthDecision {
Required,
Exempt(PeerIngressAuthExemption),
}
impl PeerIngressAuthDecision {
pub const fn is_exempt(self) -> bool {
matches!(self, Self::Exempt(_))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PeerIngressConvention {
Message,
Request {
request_id: String,
intent: String,
},
Response {
in_reply_to: InteractionId,
status: ResponseStatus,
},
Ack {
in_reply_to: InteractionId,
},
Lifecycle {
kind: PeerLifecycleKind,
peer: String,
},
PlainEvent {
source_name: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerIngressFact {
pub interaction_id: InteractionId,
pub class: PeerInputClass,
pub kind: PeerIngressKind,
pub canonical_peer_id: Option<PeerId>,
pub display_name: Option<PeerName>,
pub signing_pubkey: Option<[u8; 32]>,
pub route: Option<PeerRoute>,
pub auth: Option<PeerIngressAuthDecision>,
pub convention: PeerIngressConvention,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerIngressIdentity {
pub canonical_peer_id: PeerId,
pub display_label: String,
pub signing_pubkey: Option<[u8; 32]>,
pub convention: PeerIngressConvention,
}
impl PeerIngressIdentity {
pub fn new(
canonical_peer_id: PeerId,
display_label: impl Into<String>,
convention: PeerIngressConvention,
) -> Self {
Self {
canonical_peer_id,
display_label: display_label.into(),
signing_pubkey: None,
convention,
}
}
pub fn with_signing_pubkey(mut self, signing_pubkey: [u8; 32]) -> Self {
self.signing_pubkey = Some(signing_pubkey);
self
}
}
impl PeerIngressFact {
pub fn peer(
interaction_id: InteractionId,
class: PeerInputClass,
kind: PeerIngressKind,
auth: Option<PeerIngressAuthDecision>,
identity: PeerIngressIdentity,
) -> Self {
let PeerIngressIdentity {
canonical_peer_id,
display_label,
signing_pubkey,
convention,
} = identity;
let display_name = PeerName::new(display_label).ok();
let route = Some(match &display_name {
Some(name) => PeerRoute::with_display_name(canonical_peer_id, name.clone()),
None => PeerRoute::new(canonical_peer_id),
});
Self {
interaction_id,
class,
kind,
canonical_peer_id: Some(canonical_peer_id),
display_name,
signing_pubkey,
route,
auth,
convention,
}
}
pub fn plain_event(
interaction_id: InteractionId,
source_name: impl Into<String>,
class: PeerInputClass,
kind: PeerIngressKind,
) -> Self {
let source_name = source_name.into();
Self {
interaction_id,
class,
kind,
canonical_peer_id: None,
display_name: None,
signing_pubkey: None,
route: None,
auth: None,
convention: PeerIngressConvention::PlainEvent { source_name },
}
}
pub fn canonical_peer_id_string(&self) -> Option<String> {
self.canonical_peer_id.map(|peer_id| peer_id.as_str())
}
pub fn display_label(&self) -> Option<String> {
self.display_name.as_ref().map(PeerName::as_string)
}
pub fn diagnostic_label(&self) -> String {
self.display_label()
.or_else(|| self.canonical_peer_id_string())
.unwrap_or_else(|| "<unknown-peer-ingress>".to_string())
}
pub fn plain_event_source_name(&self) -> Option<&str> {
match &self.convention {
PeerIngressConvention::PlainEvent { source_name } => Some(source_name.as_str()),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerIngressClassification {
pub class: PeerInputClass,
pub actionable: bool,
pub kind: PeerIngressKind,
pub auth: PeerIngressAuthDecision,
pub lifecycle_kind: Option<PeerLifecycleKind>,
pub response_terminality: Option<TerminalityClass>,
}
impl PeerIngressClassification {
pub const fn required(class: PeerInputClass, kind: PeerIngressKind) -> Self {
Self {
class,
actionable: peer_input_class_actionable_grouping(class),
kind,
auth: PeerIngressAuthDecision::Required,
lifecycle_kind: None,
response_terminality: None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PeerIngressEnvelopeFacts {
pub item_id: String,
pub from_peer: String,
pub from_peer_id: PeerId,
pub kind: PeerIngressEnvelopeKind,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PeerIngressEnvelopeKind {
Message {
body: String,
},
Request {
intent: String,
params: Value,
},
Lifecycle {
kind: PeerLifecycleKind,
params: Value,
},
Response {
in_reply_to: String,
status: ResponseStatus,
result: Value,
},
Ack {
in_reply_to: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerIngressPlainEventFacts {
pub source_name: String,
pub body: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerIngressAdmission {
pub classification: PeerIngressClassification,
pub from_peer_id: Option<PeerId>,
pub lifecycle_peer: Option<String>,
pub request_id: Option<String>,
pub rendered_text: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PeerIngressReceiveFacts {
pub kind: PeerIngressKind,
pub current_phase: PeerIngressAuthorityPhase,
pub auth_required: bool,
pub auth_exempt: bool,
pub trusted: bool,
pub queued_work_present: bool,
pub queue_closed: bool,
pub queue_capacity_available: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PeerIngressReceiveAuthority {
pub outcome: PeerIngressReceiveOutcome,
pub admission_diagnostic: Option<PeerIngressAdmissionDiagnostic>,
pub authority_phase: PeerIngressAuthorityPhase,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PeerIngressReceiveOutcome {
Admitted,
DroppedUntrustedSender,
DroppedSessionClosed,
DroppedInboxFull,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PeerIngressDequeueFacts {
pub kind: PeerIngressKind,
pub auth: PeerIngressAuthDecision,
pub queued_work_remaining: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PeerIngressDequeueAuthority {
pub authority_phase: PeerIngressAuthorityPhase,
}
pub fn render_peer_ingress_admitted_text(
facts: &PeerIngressEnvelopeFacts,
classification: &PeerIngressClassification,
) -> String {
match &facts.kind {
PeerIngressEnvelopeKind::Message { body } => {
format_peer_message_projection(&facts.from_peer, body)
}
PeerIngressEnvelopeKind::Request { intent, params } => {
if classification.lifecycle_kind.is_some() {
String::new()
} else {
format_peer_request_projection(
facts.from_peer_id,
Some(&facts.from_peer),
facts.item_id.as_str(),
intent,
params,
)
}
}
PeerIngressEnvelopeKind::Lifecycle { .. } => String::new(),
PeerIngressEnvelopeKind::Response {
in_reply_to,
status,
result,
} => format_peer_response_projection(&facts.from_peer, in_reply_to, *status, result),
PeerIngressEnvelopeKind::Ack { in_reply_to } => {
format_peer_ack_projection(&facts.from_peer, in_reply_to)
}
}
}
#[derive(Debug, Clone)]
pub struct PeerInputCandidate {
pub interaction: InboxInteraction,
pub ingress: PeerIngressFact,
pub lifecycle_peer: Option<String>,
pub response_terminality: Option<TerminalityClass>,
}
impl PeerInputCandidate {
pub fn new(
interaction: InboxInteraction,
ingress: PeerIngressFact,
lifecycle_peer: Option<String>,
) -> Self {
Self {
interaction,
ingress,
lifecycle_peer,
response_terminality: None,
}
}
pub fn class(&self) -> PeerInputClass {
self.ingress.class
}
pub fn kind(&self) -> PeerIngressKind {
self.ingress.kind
}
pub fn auth(&self) -> Option<PeerIngressAuthDecision> {
self.ingress.auth
}
pub fn from_peer_id(&self) -> Option<PeerId> {
self.ingress.canonical_peer_id
}
}
pub type ClassifiedInboxInteraction = PeerInputCandidate;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PeerIngressKind {
Message,
Request,
Response,
Ack,
PlainEvent,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerIngressDiagnosticDisplay(String);
impl PeerIngressDiagnosticDisplay {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for PeerIngressDiagnosticDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PeerIngressAdmissionDiagnostic {
TrustedAtAdmission,
UntrustedAtAdmission,
}
impl PeerIngressAdmissionDiagnostic {
pub const fn from_trusted(trusted: bool) -> Self {
if trusted {
Self::TrustedAtAdmission
} else {
Self::UntrustedAtAdmission
}
}
pub const fn trusted_at_admission(self) -> bool {
matches!(self, Self::TrustedAtAdmission)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerIngressEntrySnapshot {
pub raw_item_id: InteractionId,
pub interaction_id: Option<InteractionId>,
pub class: PeerInputClass,
pub actionable: bool,
pub kind: PeerIngressKind,
pub from_peer_display: Option<PeerIngressDiagnosticDisplay>,
pub canonical_peer_id: Option<PeerId>,
pub display_name: Option<PeerName>,
pub signing_pubkey: Option<[u8; 32]>,
pub route: Option<PeerRoute>,
pub lifecycle_peer_display: Option<PeerIngressDiagnosticDisplay>,
pub request_correlation_id: Option<InteractionId>,
pub auth: Option<PeerIngressAuthDecision>,
pub admission_diagnostic: Option<PeerIngressAdmissionDiagnostic>,
pub response_terminality: Option<TerminalityClass>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PeerIngressQueueSnapshot {
pub total_count: usize,
pub actionable_count: usize,
pub response_count: usize,
pub lifecycle_count: usize,
pub silent_request_count: usize,
pub ack_count: usize,
pub plain_event_count: usize,
pub queued_entries: Vec<PeerIngressEntrySnapshot>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PeerIngressAuthorityPhase {
#[default]
Absent,
Received,
Dropped,
Delivered,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerIngressRuntimeSnapshot {
pub self_peer_id: crate::comms::PeerId,
pub auth_required: bool,
pub authority_phase: PeerIngressAuthorityPhase,
pub trusted_peers: Vec<TrustedPeerDescriptor>,
pub submission_queue_len: usize,
pub queue: PeerIngressQueueSnapshot,
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn interaction_id_json_roundtrip() {
let id = InteractionId(Uuid::new_v4());
let json = serde_json::to_string(&id).unwrap();
let parsed: InteractionId = serde_json::from_str(&json).unwrap();
assert_eq!(id, parsed);
}
#[test]
fn interaction_content_message_json_roundtrip() {
let content = InteractionContent::Message {
body: "hello".to_string(),
blocks: None,
};
let json = serde_json::to_value(&content).unwrap();
assert_eq!(json["type"], "message");
let parsed: InteractionContent = serde_json::from_value(json).unwrap();
assert_eq!(content, parsed);
}
#[test]
fn interaction_content_request_json_roundtrip() {
let content = InteractionContent::Request {
intent: "review".to_string(),
params: serde_json::json!({"pr": 42}),
blocks: None,
};
let json = serde_json::to_value(&content).unwrap();
assert_eq!(json["type"], "request");
let parsed: InteractionContent = serde_json::from_value(json).unwrap();
assert_eq!(content, parsed);
}
#[test]
fn interaction_content_response_json_roundtrip() {
let id = InteractionId(Uuid::new_v4());
let content = InteractionContent::Response {
in_reply_to: id,
status: ResponseStatus::Completed,
result: serde_json::json!({"ok": true}),
blocks: None,
};
let json = serde_json::to_value(&content).unwrap();
assert_eq!(json["type"], "response");
assert_eq!(json["status"], "completed");
let parsed: InteractionContent = serde_json::from_value(json).unwrap();
assert_eq!(content, parsed);
}
#[test]
fn response_status_json_roundtrip_all_variants() {
for (variant, expected_str) in [
(ResponseStatus::Accepted, "accepted"),
(ResponseStatus::Completed, "completed"),
(ResponseStatus::Failed, "failed"),
] {
let json = serde_json::to_value(variant).unwrap();
assert_eq!(json, expected_str);
let parsed: ResponseStatus = serde_json::from_value(json).unwrap();
assert_eq!(variant, parsed);
}
}
#[test]
fn interaction_message_with_blocks_roundtrip() {
let content = InteractionContent::Message {
body: "hello".to_string(),
blocks: Some(vec![
ContentBlock::Text {
text: "hello".to_string(),
},
ContentBlock::Image {
media_type: "image/png".to_string(),
data: "iVBORw0KGgo=".into(),
},
]),
};
let json = serde_json::to_value(&content).unwrap();
assert_eq!(json["type"], "message");
assert!(json["blocks"].is_array());
let parsed: InteractionContent = serde_json::from_value(json).unwrap();
assert_eq!(content, parsed);
}
#[test]
fn inbox_interaction_preserves_runtime_hints() {
let interaction = InboxInteraction {
id: InteractionId(Uuid::new_v4()),
from_route: None,
from: "event:webhook".into(),
content: InteractionContent::Message {
body: "hello".into(),
blocks: None,
},
rendered_text: "External event via webhook: hello".into(),
handling_mode: HandlingMode::Steer,
render_metadata: Some(RenderMetadata {
class: crate::types::RenderClass::SystemNotice,
salience: crate::types::RenderSalience::Urgent,
}),
};
assert_eq!(interaction.handling_mode, HandlingMode::Steer);
assert!(interaction.render_metadata.is_some());
}
#[test]
fn interaction_message_without_blocks_compat() {
let old_json = r#"{"type":"message","body":"hello"}"#;
let parsed: InteractionContent = serde_json::from_str(old_json).unwrap();
match parsed {
InteractionContent::Message { body, blocks } => {
assert_eq!(body, "hello");
assert_eq!(blocks, None);
}
other => panic!("Expected Message, got {other:?}"),
}
let content = InteractionContent::Message {
body: "test".to_string(),
blocks: None,
};
let json = serde_json::to_string(&content).unwrap();
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
assert!(
value.get("blocks").is_none(),
"blocks: None should not appear in JSON"
);
}
#[test]
fn actionable_grouping_mirror_matches_machine_grouping_for_all_variants() {
for (class, expected_actionable) in [
(PeerInputClass::ActionableMessage, true),
(PeerInputClass::ActionableRequest, true),
(PeerInputClass::ResponseProgress, true),
(PeerInputClass::ResponseTerminal, true),
(PeerInputClass::PlainEvent, true),
(PeerInputClass::PeerLifecycleKickoffFailed, true),
(PeerInputClass::PeerLifecycleKickoffCancelled, true),
(PeerInputClass::PeerLifecycleAdded, false),
(PeerInputClass::PeerLifecycleRetired, false),
(PeerInputClass::PeerLifecycleUnwired, false),
(PeerInputClass::SilentRequest, false),
(PeerInputClass::Ack, false),
] {
assert_eq!(
peer_input_class_actionable_grouping(class),
expected_actionable,
"actionable grouping verdict drifted for {class:?}"
);
}
fn assert_variant_covered(class: PeerInputClass) {
match class {
PeerInputClass::ActionableMessage
| PeerInputClass::ActionableRequest
| PeerInputClass::ResponseProgress
| PeerInputClass::ResponseTerminal
| PeerInputClass::PlainEvent
| PeerInputClass::PeerLifecycleKickoffFailed
| PeerInputClass::PeerLifecycleKickoffCancelled
| PeerInputClass::PeerLifecycleAdded
| PeerInputClass::PeerLifecycleRetired
| PeerInputClass::PeerLifecycleUnwired
| PeerInputClass::SilentRequest
| PeerInputClass::Ack => (),
}
}
assert_variant_covered(PeerInputClass::Ack);
}
}