use super::connection::WireAuthBindingRef;
use super::live::{
LiveCloseStatus, LiveCommitInputStatus, LiveInterruptStatus, LiveOpenResult, LiveOpenTransport,
LiveRefreshStatus, LiveTruncateStatus, WireLiveAdapterStatus,
};
use super::mob::{
WireControlScope, WireMemberHistoryPageBody, WireMemberLaunchMode, WireMobRuntimeMode,
WireTrustedPeerIdentity,
};
use super::portable_spec::{PortableMemberSpec, WireResolvedToolAccessPolicy};
use super::realtime::RealtimeTurningMode;
use meerkat_core::comms::{PeerAddress, PeerId, PeerName, TrustedPeerDescriptor};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use std::fmt;
pub use meerkat_core::comms::SUPERVISOR_BRIDGE_INTENT;
pub const SUPERVISOR_BRIDGE_BOOTSTRAP_TOKEN_PARAM: &str = "mob_supervisor_bootstrap_token";
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct BridgeProtocolVersion(u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UnsupportedBridgeProtocolVersion {
raw: u32,
command: Option<&'static str>,
minimum: Option<u32>,
}
impl fmt::Display for UnsupportedBridgeProtocolVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (self.command, self.minimum) {
(Some(command), Some(minimum)) => write!(
f,
"unsupported supervisor bridge protocol version {} for {command} (minimum {minimum}; supported {:?}; default {})",
self.raw,
BridgeProtocolVersion::SUPPORTED,
BridgeProtocolVersion::DEFAULT
),
_ => write!(
f,
"unsupported supervisor bridge protocol version {} (supported {:?}; default {})",
self.raw,
BridgeProtocolVersion::SUPPORTED,
BridgeProtocolVersion::DEFAULT
),
}
}
}
impl std::error::Error for UnsupportedBridgeProtocolVersion {}
impl BridgeProtocolVersion {
pub const V2: Self = Self(2);
pub const V3: Self = Self(3);
pub const V4: Self = Self(4);
pub const CURRENT: Self = Self::V4;
pub const DEFAULT: Self = Self::V4;
pub const SUPPORTED: &'static [Self] = &[Self::V2, Self::V3, Self::V4];
pub const fn is_supported(self) -> bool {
matches!(self.0, 2..=4)
}
pub const fn same_protocol_as(self, other: Self) -> bool {
self.0 == other.0
}
pub const fn supports_peer_overlay(self) -> bool {
self.0 >= 3
}
pub const fn supports_multi_host(self) -> bool {
self.0 >= 4
}
pub fn supported() -> &'static [Self] {
Self::SUPPORTED
}
fn from_supported_u32(raw: u32) -> Result<Self, UnsupportedBridgeProtocolVersion> {
match raw {
2 => Ok(Self::V2),
3 => Ok(Self::V3),
4 => Ok(Self::V4),
_ => Err(UnsupportedBridgeProtocolVersion {
raw,
command: None,
minimum: None,
}),
}
}
fn insufficient_for_command(
self,
command: &'static str,
minimum: Self,
) -> UnsupportedBridgeProtocolVersion {
UnsupportedBridgeProtocolVersion {
raw: self.0,
command: Some(command),
minimum: Some(minimum.0),
}
}
}
impl Default for BridgeProtocolVersion {
fn default() -> Self {
Self::DEFAULT
}
}
impl fmt::Debug for BridgeProtocolVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for BridgeProtocolVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl TryFrom<u32> for BridgeProtocolVersion {
type Error = UnsupportedBridgeProtocolVersion;
fn try_from(raw: u32) -> Result<Self, Self::Error> {
Self::from_supported_u32(raw)
}
}
impl Serialize for BridgeProtocolVersion {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u32(self.0)
}
}
impl<'de> Deserialize<'de> for BridgeProtocolVersion {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let raw = u32::deserialize(deserializer)?;
Self::from_supported_u32(raw).map_err(de::Error::custom)
}
}
pub const SUPERVISOR_BRIDGE_PROTOCOL_VERSION: BridgeProtocolVersion =
BridgeProtocolVersion::CURRENT;
pub const SUPERVISOR_BRIDGE_CURRENT_PROTOCOL_VERSION: BridgeProtocolVersion =
BridgeProtocolVersion::CURRENT;
pub const SUPERVISOR_BRIDGE_DEFAULT_PROTOCOL_VERSION: BridgeProtocolVersion =
BridgeProtocolVersion::DEFAULT;
pub const SUPERVISOR_BRIDGE_SUPPORTED_PROTOCOL_VERSIONS: &[BridgeProtocolVersion] =
BridgeProtocolVersion::SUPPORTED;
pub const fn supervisor_bridge_current_protocol_version() -> BridgeProtocolVersion {
SUPERVISOR_BRIDGE_CURRENT_PROTOCOL_VERSION
}
pub const fn supervisor_bridge_default_protocol_version() -> BridgeProtocolVersion {
SUPERVISOR_BRIDGE_DEFAULT_PROTOCOL_VERSION
}
pub fn supervisor_bridge_supported_protocol_versions() -> &'static [BridgeProtocolVersion] {
SUPERVISOR_BRIDGE_SUPPORTED_PROTOCOL_VERSIONS
}
pub fn supervisor_bridge_protocol_version_supported(
protocol_version: BridgeProtocolVersion,
) -> bool {
protocol_version.is_supported()
}
fn default_supported_protocol_versions() -> Vec<BridgeProtocolVersion> {
supervisor_bridge_supported_protocol_versions().to_vec()
}
fn bool_is_false(value: &bool) -> bool {
!*value
}
pub fn canonicalize_bridge_address(address: &str) -> String {
let Some((base, query)) = address.split_once('?') else {
return address.to_string();
};
let filtered: Vec<&str> = query
.split('&')
.filter(|pair| {
pair.split_once('=')
.map(|(key, _)| key != SUPERVISOR_BRIDGE_BOOTSTRAP_TOKEN_PARAM)
.unwrap_or(true)
})
.filter(|pair| !pair.is_empty())
.collect();
if filtered.is_empty() {
base.to_string()
} else {
format!("{base}?{}", filtered.join("&"))
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "delivery", rename_all = "snake_case", deny_unknown_fields)]
#[non_exhaustive]
pub enum BridgeSupervisorDelivery {
SubmitSupervisorRotation(BridgeSupervisorRotationSubmit),
}
impl BridgeSupervisorDelivery {
pub fn protocol_version(&self) -> BridgeProtocolVersion {
match self {
Self::SubmitSupervisorRotation(payload) => payload.protocol_version,
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "command", rename_all = "snake_case", deny_unknown_fields)]
#[non_exhaustive]
pub enum BridgeCommand {
BindMember(BridgeBindPayload),
AuthorizeSupervisor(BridgeSupervisorPayload),
RevokeSupervisor(BridgeSupervisorPayload),
DeliverMemberInput(BridgeDeliveryPayload),
ObserveMember(BridgeSupervisorPayload),
InterruptMember(BridgeInterruptPayload),
HardCancelMember(BridgeHardCancelPayload),
CancelTrackedMemberInput(BridgeTrackedInputCancelPayload),
RetireMember(BridgeSupervisorPayload),
DestroyMember(BridgeSupervisorPayload),
WireMember(BridgePeerWiringPayload),
UnwireMember(BridgePeerWiringPayload),
DeclareMemberOutboundTaint(BridgeOutboundTaintPayload),
ReadMemberHistory(BridgeReadHistoryPayload),
PollMemberEvents(BridgePollEventsPayload),
OpenMemberLiveChannel(BridgeLiveOpenPayload),
CloseMemberLiveChannel(BridgeLiveChannelPayload),
MemberLiveChannelStatus(BridgeLiveStatusPayload),
ControlMemberLiveChannel(BridgeLiveControlPayload),
BindHost(BridgeHostBindPayload),
RebindHost(BridgeHostRebindPayload),
RevokeHost(BridgeHostRevokePayload),
MaterializeMember(Box<BridgeMaterializePayload>),
ReleaseMember(BridgeReleasePayload),
InstallPeerTrust(BridgePeerTrustPayload),
RemovePeerTrust(BridgePeerTrustPayload),
HostStatus(BridgeHostStatusPayload),
MemberOperatorRequest(BridgeMemberOperatorPayload),
ObserveSupervisorRotation(BridgeSupervisorRotationObserve),
}
impl BridgeCommand {
pub fn protocol_version(&self) -> BridgeProtocolVersion {
match self {
Self::BindMember(payload) => payload.protocol_version,
Self::AuthorizeSupervisor(payload)
| Self::RevokeSupervisor(payload)
| Self::ObserveMember(payload)
| Self::RetireMember(payload)
| Self::DestroyMember(payload) => payload.protocol_version,
Self::InterruptMember(payload) => payload.protocol_version,
Self::HardCancelMember(payload) => payload.protocol_version,
Self::CancelTrackedMemberInput(payload) => payload.protocol_version,
Self::DeliverMemberInput(payload) => payload.protocol_version,
Self::WireMember(payload) | Self::UnwireMember(payload) => payload.protocol_version,
Self::DeclareMemberOutboundTaint(payload) => payload.protocol_version,
Self::ReadMemberHistory(payload) => payload.protocol_version,
Self::PollMemberEvents(payload) => payload.protocol_version,
Self::OpenMemberLiveChannel(payload) => payload.protocol_version,
Self::CloseMemberLiveChannel(payload) => payload.protocol_version,
Self::MemberLiveChannelStatus(payload) => payload.protocol_version,
Self::ControlMemberLiveChannel(payload) => payload.protocol_version,
Self::BindHost(payload) => payload.protocol_version,
Self::RebindHost(payload) => payload.protocol_version,
Self::RevokeHost(payload) => payload.protocol_version,
Self::MaterializeMember(payload) => payload.protocol_version,
Self::ReleaseMember(payload) => payload.protocol_version,
Self::InstallPeerTrust(payload) | Self::RemovePeerTrust(payload) => {
payload.protocol_version
}
Self::HostStatus(payload) => payload.protocol_version,
Self::MemberOperatorRequest(payload) => payload.protocol_version,
Self::ObserveSupervisorRotation(payload) => payload.protocol_version,
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeOutboundTaintPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target: Option<BridgeOutboundTaintTarget>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub taint: Option<meerkat_core::comms::SenderContentTaint>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BridgeOutboundTaintTarget {
PeerOnly,
Placed(BridgeMemberIncarnation),
}
#[derive(Debug)]
pub enum BridgeCommandDecodeError {
UnsupportedProtocolVersion(UnsupportedBridgeProtocolVersion),
Invalid(serde_json::Error),
}
impl fmt::Display for BridgeCommandDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnsupportedProtocolVersion(error) => error.fmt(f),
Self::Invalid(error) => error.fmt(f),
}
}
}
impl std::error::Error for BridgeCommandDecodeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::UnsupportedProtocolVersion(error) => Some(error),
Self::Invalid(error) => Some(error),
}
}
}
pub fn decode_bridge_command(
value: serde_json::Value,
) -> Result<BridgeCommand, BridgeCommandDecodeError> {
let version = value
.get("protocol_version")
.and_then(serde_json::Value::as_u64)
.and_then(|raw| u32::try_from(raw).ok())
.map(BridgeProtocolVersion::from_supported_u32)
.transpose()
.map_err(BridgeCommandDecodeError::UnsupportedProtocolVersion)?;
if let Some((command, minimum)) = bridge_command_minimum_protocol(&value)
&& let Some(version) = version
&& version < minimum
{
return Err(BridgeCommandDecodeError::UnsupportedProtocolVersion(
version.insufficient_for_command(command, minimum),
));
}
serde_json::from_value(value).map_err(BridgeCommandDecodeError::Invalid)
}
fn bridge_command_minimum_protocol(
value: &serde_json::Value,
) -> Option<(&'static str, BridgeProtocolVersion)> {
let command = value.get("command")?.as_str()?;
let minimum = match command {
"hard_cancel_member"
| "cancel_tracked_member_input"
| "read_member_history"
| "poll_member_events"
| "open_member_live_channel"
| "close_member_live_channel"
| "member_live_channel_status"
| "control_member_live_channel"
| "bind_host"
| "rebind_host"
| "revoke_host"
| "materialize_member"
| "release_member"
| "install_peer_trust"
| "remove_peer_trust"
| "host_status"
| "member_operator_request"
| "observe_supervisor_rotation" => BridgeProtocolVersion::V4,
"deliver_member_input"
if value
.get("outcome_tracking")
.is_some_and(|tracking| !tracking.is_null())
|| value
.get("transcript_interaction_id")
.is_some_and(|interaction_id| !interaction_id.is_null()) =>
{
BridgeProtocolVersion::V4
}
"deliver_member_input"
if value.get("turn").is_some_and(|turn| !turn.is_null())
|| value
.get("expected_member")
.is_some_and(|expected| !expected.is_null()) =>
{
BridgeProtocolVersion::V4
}
"interrupt_member"
if value
.get("expected_member")
.is_some_and(|expected| !expected.is_null()) =>
{
BridgeProtocolVersion::V4
}
"declare_member_outbound_taint" if value.get("target").is_some() => {
BridgeProtocolVersion::V4
}
_ => return None,
};
let command = match command {
"hard_cancel_member" => "HardCancelMember",
"cancel_tracked_member_input" => "CancelTrackedMemberInput",
"read_member_history" => "ReadMemberHistory",
"poll_member_events" => "PollMemberEvents",
"open_member_live_channel" => "OpenMemberLiveChannel",
"close_member_live_channel" => "CloseMemberLiveChannel",
"member_live_channel_status" => "MemberLiveChannelStatus",
"control_member_live_channel" => "ControlMemberLiveChannel",
"bind_host" => "BindHost",
"rebind_host" => "RebindHost",
"revoke_host" => "RevokeHost",
"materialize_member" => "MaterializeMember",
"release_member" => "ReleaseMember",
"install_peer_trust" => "InstallPeerTrust",
"remove_peer_trust" => "RemovePeerTrust",
"host_status" => "HostStatus",
"member_operator_request" => "MemberOperatorRequest",
"observe_supervisor_rotation" => "ObserveSupervisorRotation",
"deliver_member_input" => "DeliverMemberInput(V4 extension)",
"interrupt_member" => "InterruptMember(V4 extension)",
"declare_member_outbound_taint" => "DeclareMemberOutboundTaint(V4 target)",
_ => unreachable!("minimum-version command table is exhaustive"),
};
Some((command, minimum))
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeReadHistoryPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_member: BridgeMemberIncarnation,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub from_index: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgePollEventsPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_member: BridgeMemberIncarnation,
pub cursor: BridgeEventCursor,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max: Option<u32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub outcome_acks: Vec<BridgeTurnOutcomeAck>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_outcomes: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub wait_ms: Option<u32>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "cursor", rename_all = "snake_case", deny_unknown_fields)]
pub enum BridgeEventCursor {
Tail,
At { generation: u64, seq: u64 },
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MemberEventCursor {
pub generation: u64,
pub seq: u64,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeLiveOpenPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_member: BridgeMemberIncarnation,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub turning_mode: Option<RealtimeTurningMode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transport: Option<LiveOpenTransport>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeLiveChannelPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_member: BridgeMemberIncarnation,
pub channel_id: String,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeLiveStatusPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_member: BridgeMemberIncarnation,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub channel_id: Option<String>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeLiveControlPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_member: BridgeMemberIncarnation,
pub channel_id: String,
pub verb: BridgeLiveControlVerb,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "verb", rename_all = "snake_case", deny_unknown_fields)]
pub enum BridgeLiveControlVerb {
CommitInput,
Interrupt,
Truncate {
item_id: String,
content_index: u32,
audio_played_ms: u64,
},
Refresh,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostCapabilityRequirements {
pub durable_sessions: bool,
pub autonomous_members: bool,
pub tracked_input_cancel: bool,
pub protocol_v4: bool,
}
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(transparent)]
pub struct BridgeHostBootstrapProof(String);
impl BridgeHostBootstrapProof {
pub fn new(proof: impl Into<String>) -> Self {
Self(proof.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl std::fmt::Debug for BridgeHostBootstrapProof {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0.is_empty() {
write!(f, "BridgeHostBootstrapProof(empty)")
} else {
write!(f, "BridgeHostBootstrapProof(<redacted, {}B>)", self.0.len())
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostBindPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub binding_generation: u64,
pub protocol_version: BridgeProtocolVersion,
pub mob_id: String,
pub expected_host_peer_id: String,
pub expected_address: String,
pub bootstrap_proof: BridgeHostBootstrapProof,
pub required_capabilities: BridgeHostCapabilityRequirements,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostRebindPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub binding_generation: u64,
pub protocol_version: BridgeProtocolVersion,
pub mob_id: String,
pub required_capabilities: BridgeHostCapabilityRequirements,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostRevokePayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub binding_generation: u64,
pub protocol_version: BridgeProtocolVersion,
pub mob_id: String,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeMaterializePayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub binding_generation: u64,
pub protocol_version: BridgeProtocolVersion,
pub generation: u64,
pub fence_token: u64,
pub spec: PortableMemberSpec,
pub spec_digest: String,
pub launch: MaterializeLaunchMode,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "mode", rename_all = "snake_case", deny_unknown_fields)]
pub enum MaterializeLaunchMode {
Fresh {},
Resume { session_id: String },
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MaterializeLaunchOutcome {
Fresh,
ResumedLive,
ResumedFromSnapshot,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeReleasePayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub binding_generation: u64,
pub protocol_version: BridgeProtocolVersion,
pub mob_id: String,
pub agent_identity: String,
pub generation: u64,
pub fence_token: u64,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgePeerTrustPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub binding_generation: u64,
pub protocol_version: BridgeProtocolVersion,
pub mob_id: String,
pub agent_identity: String,
pub peer: BridgePeerSpec,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostStatusPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub binding_generation: u64,
pub protocol_version: BridgeProtocolVersion,
pub mob_id: String,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeMemberOperatorPayload {
pub agent_identity: String,
pub requester_generation: u64,
pub requester_fence_token: u64,
pub requester_host_id: String,
pub requester_host_binding_generation: u64,
pub requester_member_session_id: String,
pub request_id: String,
pub op: MemberOperatorOp,
pub protocol_version: BridgeProtocolVersion,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "snake_case", deny_unknown_fields)]
pub enum MemberOperatorOp {
SpawnMember(Box<MemberOperatorSpawnSpec>),
SpawnManyMembers {
specs: Vec<MemberOperatorSpawnSpec>,
},
RetireMember {
member_id: String,
},
ForceCancelMember {
member_id: String,
},
MemberStatus {
member_id: String,
},
WireMembers {
member_id: String,
peer_member_id: String,
},
UnwireMembers {
member_id: String,
peer_member_id: String,
},
ListMembers,
MobListFlows,
MobRunFlow {
flow_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
params: Option<WireOpaqueJson>,
},
MobFlowStatus {
run_id: String,
},
MobCancelFlow {
run_id: String,
},
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MemberOperatorSpawnSpec {
pub profile: String,
pub member_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initial_message: Option<meerkat_core::types::ContentInput>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime_mode: Option<WireMobRuntimeMode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub launch_mode: Option<WireMemberLaunchMode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auto_wire_parent: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub placement: Option<String>,
pub requested_tool_access_policy_present: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resolved_tool_access_policy: Option<WireResolvedToolAccessPolicy>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MemberOperatorReply {
pub request_id: String,
pub outcome: MemberOperatorOutcome,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "outcome", rename_all = "snake_case", deny_unknown_fields)]
pub enum MemberOperatorOutcome {
Completed { result: WireOpaqueJson },
Rejected {
cause: BridgeRejectionCause,
reason: String,
},
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct WireOpaqueJson(String);
impl WireOpaqueJson {
pub fn from_value(value: &serde_json::Value) -> Self {
Self(value.to_string())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn to_value(&self) -> Result<serde_json::Value, serde_json::Error> {
serde_json::from_str(&self.0)
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeTurnDirective {
pub correlation: BridgeTurnCorrelation,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_overlay: Option<meerkat_core::service::PublicTurnToolOverlay>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeMemberIncarnation {
pub mob_id: String,
pub agent_identity: String,
pub host_id: String,
pub binding_generation: u64,
pub member_session_id: String,
pub generation: u64,
pub fence_token: u64,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeTurnCorrelation {
pub run_id: String,
pub step_id: String,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeTurnOutcomeRecord {
pub input_id: String,
pub generation: u64,
pub fence_token: u64,
pub terminal_seq: u64,
pub outcome: WireFlowTurnOutcome,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeTurnOutcomeAck {
pub generation: u64,
pub fence_token: u64,
pub input_id: String,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WireFlowTurnOutcome {
RunCompleted,
ExtractionSucceeded,
ExtractionFailed { detail: WireFlowFailureDetail },
RunFailed { detail: WireFlowFailureDetail },
InteractionComplete,
InteractionCallbackPending,
InteractionFailed { detail: WireFlowFailureDetail },
ChannelClosed,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WireFlowFailureDetail {
pub text: String,
pub original_utf8_bytes: u64,
pub truncated: bool,
}
impl WireFlowFailureDetail {
#[must_use]
pub fn complete(text: String) -> Self {
Self {
original_utf8_bytes: u64::try_from(text.len()).unwrap_or(u64::MAX),
text,
truncated: false,
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WireHostBindingDescriptorKind {
Host,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WireHostBindingDescriptor {
pub kind: WireHostBindingDescriptorKind,
pub address: String,
pub identity: WireTrustedPeerIdentity,
pub bootstrap_token: BridgeBootstrapToken,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub live_endpoint: Option<String>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "result", rename_all = "snake_case", deny_unknown_fields)]
#[non_exhaustive]
pub enum BridgeReply {
BindMember(BridgeBindResponse),
Ack(BridgeAck),
Observation(BridgeObservationResponse),
Delivery(BridgeDeliveryResponse),
TrackedInputCancelled(BridgeTrackedInputCancelResponse),
Retire(BridgeRetireResponse),
Destroy(BridgeDestroyResponse),
SupervisorRotation(BridgeSupervisorRotationObservation),
Rejected {
cause: BridgeRejectionCause,
reason: String,
},
BindHost(BridgeHostBindResponse),
HostRebound(BridgeHostReboundResponse),
HostRevoked(BridgeHostRevokedResponse),
MemberHistoryPage(BridgeMemberHistoryPage),
MemberEventsPage(BridgeMemberEventsPage),
MemberMaterialized(BridgeMaterializedResponse),
MemberReleased(BridgeMemberReleasedResponse),
HostStatus(BridgeHostStatusResponse),
MemberLiveChannelOpened(BridgeLiveOpenedResponse),
MemberLiveChannelClosed {
status: LiveCloseStatus,
},
MemberLiveChannelStatusReport {
channel_id: String,
status: WireLiveAdapterStatus,
},
MemberLiveChannelControlled(BridgeLiveControlledResponse),
MemberOperatorReply(MemberOperatorReply),
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeLiveOpenedResponse {
pub open: LiveOpenResult,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeLiveControlledResponse {
pub outcome: BridgeLiveControlOutcome,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostBindResponse {
pub host_peer_id: String,
pub binding_generation: u64,
pub address: String,
pub capabilities: BridgeCapabilities,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub live_endpoint: Option<String>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostReboundResponse {
pub host_peer_id: String,
pub binding_generation: u64,
pub capabilities: BridgeCapabilities,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub live_endpoint: Option<String>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostRevokedResponse {
pub host_peer_id: String,
pub mob_id: String,
pub epoch: u64,
pub binding_generation: u64,
pub released_members: Vec<String>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeMemberHistoryPage {
pub generation: u64,
pub page: WireMemberHistoryPageBody,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeMemberEventsPage {
pub runtime_incarnation: BridgeHostRuntimeIncarnation,
pub generation: u64,
pub fence_token: u64,
pub events: Vec<WireEventRow>,
pub from_seq: u64,
pub next_seq: u64,
pub watermark: u64,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub turn_outcomes: Vec<BridgeTurnOutcomeRecord>,
pub outcomes_complete: bool,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WireEventRow {
pub durable_seq: u64,
pub envelope: meerkat_core::EventEnvelope<meerkat_core::AgentEvent>,
}
impl PartialEq for WireEventRow {
fn eq(&self, other: &Self) -> bool {
if self.durable_seq != other.durable_seq {
return false;
}
match (
serde_json::to_value(&self.envelope),
serde_json::to_value(&other.envelope),
) {
(Ok(a), Ok(b)) => a == b,
_ => false,
}
}
}
impl Eq for WireEventRow {}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeMaterializedResponse {
pub member_pubkey: String,
pub member_peer_id: String,
pub advertised_address: String,
pub session_id: String,
pub spec_digest: String,
pub engine_version: String,
pub launch_outcome: MaterializeLaunchOutcome,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resolved_auth_binding: Option<WireAuthBindingRef>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeMemberReleasedResponse {
pub disposal: MemberSessionDisposal,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "disposal", rename_all = "snake_case")]
pub enum MemberSessionDisposal {
Archived,
AlreadyArchived,
RuntimeReleasedOnly { cause: RuntimeReleaseCause },
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeReleaseCause {
HostOwnedSession,
NoDurableSessions,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(transparent)]
pub struct BridgeHostRuntimeIncarnation(
#[cfg_attr(feature = "schema", schemars(with = "String"))] uuid::Uuid,
);
impl BridgeHostRuntimeIncarnation {
#[must_use]
pub fn new() -> Self {
Self(uuid::Uuid::new_v4())
}
#[must_use]
pub const fn from_uuid(value: uuid::Uuid) -> Self {
Self(value)
}
#[must_use]
pub const fn as_uuid(&self) -> uuid::Uuid {
self.0
}
}
impl Default for BridgeHostRuntimeIncarnation {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for BridgeHostRuntimeIncarnation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl std::str::FromStr for BridgeHostRuntimeIncarnation {
type Err = uuid::Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
uuid::Uuid::parse_str(value).map(Self)
}
}
impl From<uuid::Uuid> for BridgeHostRuntimeIncarnation {
fn from(value: uuid::Uuid) -> Self {
Self::from_uuid(value)
}
}
impl From<BridgeHostRuntimeIncarnation> for uuid::Uuid {
fn from(value: BridgeHostRuntimeIncarnation) -> Self {
value.0
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostStatusResponse {
pub runtime_incarnation: BridgeHostRuntimeIncarnation,
pub members: Vec<BridgeHostMemberRecord>,
pub capabilities: BridgeCapabilities,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BridgeHostMemberRecord {
pub agent_identity: String,
pub generation: u64,
pub fence_token: u64,
pub session_id: String,
pub spec_digest: String,
pub healthy: bool,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "verb", rename_all = "snake_case", deny_unknown_fields)]
pub enum BridgeLiveControlOutcome {
CommitInput { status: LiveCommitInputStatus },
Interrupt { status: LiveInterruptStatus },
Truncate { status: LiveTruncateStatus },
Refresh { status: LiveRefreshStatus },
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum BridgeRejectionReply {
Typed {
cause: BridgeRejectionCause,
reason: String,
},
LegacyV1RawString {
reason: String,
},
}
impl BridgeRejectionReply {
pub fn reason(&self) -> &str {
match self {
Self::Typed { reason, .. } | Self::LegacyV1RawString { reason } => reason,
}
}
pub fn typed_cause(&self) -> Option<BridgeRejectionCause> {
match self {
Self::Typed { cause, .. } => Some(cause.clone()),
Self::LegacyV1RawString { .. } => None,
}
}
pub fn is_legacy_v1_raw_string(&self) -> bool {
matches!(self, Self::LegacyV1RawString { .. })
}
}
pub fn decode_protocol_v2_bridge_rejection(
value: &serde_json::Value,
) -> Option<BridgeRejectionReply> {
match serde_json::from_value::<BridgeReply>(value.clone()).ok()? {
BridgeReply::Rejected { cause, reason } => {
Some(BridgeRejectionReply::Typed { cause, reason })
}
_ => None,
}
}
pub fn decode_legacy_v1_raw_string_rejection(
value: &serde_json::Value,
) -> Option<BridgeRejectionReply> {
value
.as_str()
.map(|reason| BridgeRejectionReply::LegacyV1RawString {
reason: reason.to_string(),
})
}
pub fn decode_bridge_rejection_reply(
protocol_version: BridgeProtocolVersion,
value: &serde_json::Value,
) -> Option<BridgeRejectionReply> {
let _ = protocol_version;
decode_protocol_v2_bridge_rejection(value)
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum BridgeRejectionCause {
NotBound,
StaleSupervisor,
SenderMismatch,
AlreadyBound,
InvalidBootstrapToken,
UnsupportedProtocolVersion,
InvalidSupervisorSpec,
InvalidPeerSpec,
AddressMismatch,
Unsupported,
Internal,
StaleFence,
StaleCursor { watermark: u64, generation: u64 },
OversizedEvent {
generation: u64,
durable_seq: u64,
next_seq: u64,
encoded_bytes: u64,
max_bytes: u64,
},
HistoryRowTooLarge {
index: u64,
encoded_bytes: u64,
max_bytes: u64,
},
Unavailable,
ScopeDenied {
required: WireControlScope,
presented: Vec<WireControlScope>,
},
SpecDigestMismatch,
MaterializeBuildRejected { cause: MemberBuildRejection },
ModelUnresolvable { model: String },
AuthBindingUnresolvable { realm: String, binding: String },
McpCommandMissing { server: String },
RealmBackendUnavailable,
EnvKeyMissing { key: String },
HostEngineVersionChanged { bound: String, reported: String },
ModelNotRealtime { model: String, provider: String },
LiveAdapterUnavailable { provider: String },
LiveTransportUnavailable,
LiveChannelAlreadyBound,
LiveChannelNotFound,
LiveTransportUnsupported { requested: String },
ResumeSessionNotFound,
CapabilityMissing { capability: String },
LaunchModeUnsupported,
LaunchModePlacementMismatch,
SessionOwnershipConflict { session_id: String },
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum MemberBuildRejection {
UnknownProviderForModel { model: String },
BindingUnresolvable { kind: ConnectionTargetErrorKind },
ProviderAuth { kind: meerkat_core::AuthErrorKind },
SelfHostedServerMissing { server_id: String },
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ConnectionTargetErrorKind {
MissingRealm,
UnknownRealm,
MissingDefaultBinding,
InvalidRealmId,
InvalidBindingId,
RealmConfigInvalid,
BindingInvalid,
ProviderMismatch,
RealmChain,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum BridgeMemberRuntimeState {
Initializing,
Idle,
Attached,
Running,
Retired,
Stopped,
Destroyed,
}
impl std::fmt::Display for BridgeMemberRuntimeState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Initializing => write!(f, "initializing"),
Self::Idle => write!(f, "idle"),
Self::Attached => write!(f, "attached"),
Self::Running => write!(f, "running"),
Self::Retired => write!(f, "retired"),
Self::Stopped => write!(f, "stopped"),
Self::Destroyed => write!(f, "destroyed"),
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgePeerSpec {
pub name: String,
pub peer_id: String,
pub address: String,
#[serde(default)]
pub pubkey: [u8; 32],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BridgePeerPubKey([u8; 32]);
impl BridgePeerPubKey {
pub const fn new(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub const fn into_bytes(self) -> [u8; 32] {
self.0
}
pub const fn is_zero(&self) -> bool {
let mut index = 0;
while index < self.0.len() {
if self.0[index] != 0 {
return false;
}
index += 1;
}
true
}
pub fn derived_peer_id(&self) -> PeerId {
PeerId::from_ed25519_pubkey(&self.0)
}
}
impl From<[u8; 32]> for BridgePeerPubKey {
fn from(bytes: [u8; 32]) -> Self {
Self::new(bytes)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BridgePeerIdentity {
pub name: PeerName,
pub peer_id: PeerId,
pub address: PeerAddress,
pub pubkey: BridgePeerPubKey,
}
impl BridgePeerIdentity {
pub fn into_trusted_peer_descriptor(self) -> TrustedPeerDescriptor {
TrustedPeerDescriptor {
peer_id: self.peer_id,
name: self.name,
address: self.address,
pubkey: self.pubkey.into_bytes(),
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum BridgePeerConnectivity {
Reachable,
Unreachable,
Unknown,
}
impl From<TrustedPeerDescriptor> for BridgePeerSpec {
fn from(spec: TrustedPeerDescriptor) -> Self {
Self {
name: spec.name.as_str().to_string(),
peer_id: spec.peer_id.as_str(),
address: spec.address.to_string(),
pubkey: spec.pubkey,
}
}
}
impl TryFrom<BridgePeerSpec> for meerkat_core::comms::TrustedPeerDescriptor {
type Error = String;
fn try_from(spec: BridgePeerSpec) -> Result<Self, Self::Error> {
Self::try_from(&spec)
}
}
impl TryFrom<&BridgePeerSpec> for BridgePeerIdentity {
type Error = String;
fn try_from(spec: &BridgePeerSpec) -> Result<Self, Self::Error> {
let peer_id = PeerId::parse(&spec.peer_id).map_err(|e| format!("invalid peer_id: {e}"))?;
let name =
PeerName::new(spec.name.clone()).map_err(|e| format!("invalid peer name: {e}"))?;
let address = parse_peer_address(&spec.address)?;
let pubkey = BridgePeerPubKey::new(spec.pubkey);
if pubkey.is_zero() {
return Err("peer pubkey must be non-zero".to_string());
}
let derived = pubkey.derived_peer_id();
if derived != peer_id {
return Err(format!(
"peer_id {peer_id} does not match pubkey-derived id {derived}"
));
}
Ok(Self {
name,
peer_id,
address,
pubkey,
})
}
}
impl TryFrom<&BridgePeerSpec> for TrustedPeerDescriptor {
type Error = String;
fn try_from(spec: &BridgePeerSpec) -> Result<Self, Self::Error> {
BridgePeerIdentity::try_from(spec).map(BridgePeerIdentity::into_trusted_peer_descriptor)
}
}
fn parse_peer_address(raw: &str) -> Result<PeerAddress, String> {
PeerAddress::parse(raw).map_err(|err| err.to_string())
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeSupervisorPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(transparent)]
pub struct SupervisorRotationOperationId(
#[cfg_attr(feature = "schema", schemars(with = "String"))] uuid::Uuid,
);
impl SupervisorRotationOperationId {
#[must_use]
pub fn new() -> Self {
Self(uuid::Uuid::new_v4())
}
#[must_use]
pub const fn from_uuid(value: uuid::Uuid) -> Self {
Self(value)
}
#[must_use]
pub const fn as_uuid(&self) -> uuid::Uuid {
self.0
}
}
impl Default for SupervisorRotationOperationId {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for SupervisorRotationOperationId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl std::str::FromStr for SupervisorRotationOperationId {
type Err = uuid::Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
uuid::Uuid::parse_str(value).map(Self)
}
}
impl From<uuid::Uuid> for SupervisorRotationOperationId {
fn from(value: uuid::Uuid) -> Self {
Self::from_uuid(value)
}
}
impl From<SupervisorRotationOperationId> for uuid::Uuid {
fn from(value: SupervisorRotationOperationId) -> Self {
value.0
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeSupervisorRotationSubmit {
pub operation_id: SupervisorRotationOperationId,
pub target: BridgePeerSpec,
pub target_epoch: u64,
pub protocol_version: BridgeProtocolVersion,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeSupervisorRotationObserve {
pub operation_id: SupervisorRotationOperationId,
pub observer: BridgePeerSpec,
pub observer_epoch: u64,
pub protocol_version: BridgeProtocolVersion,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum BridgeSupervisorRotationPendingPhase {
PreviousRevokePending,
NextPublishPending,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeSupervisorRotationTargetReceipt {
pub target: BridgePeerSpec,
pub target_epoch: u64,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeSupervisorRotationOperationReceipt {
pub operation_id: SupervisorRotationOperationId,
pub target: BridgeSupervisorRotationTargetReceipt,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum BridgeSupervisorRotationRejectionCause {
OperationConflict,
InvalidTarget,
StaleTargetEpoch,
SenderMismatch,
UnsupportedProtocolVersion,
Internal,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeSupervisorRotationRejectionReceipt {
pub operation: BridgeSupervisorRotationOperationReceipt,
pub cause: BridgeSupervisorRotationRejectionCause,
pub reason: String,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "status", rename_all = "snake_case", deny_unknown_fields)]
#[non_exhaustive]
pub enum BridgeSupervisorRotationState {
Pending {
operation: BridgeSupervisorRotationOperationReceipt,
phase: BridgeSupervisorRotationPendingPhase,
},
Completed {
receipt: BridgeSupervisorRotationOperationReceipt,
},
Rejected {
receipt: BridgeSupervisorRotationRejectionReceipt,
},
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "outcome", rename_all = "snake_case", deny_unknown_fields)]
#[non_exhaustive]
pub enum BridgeSupervisorRotationObservation {
Found {
state: BridgeSupervisorRotationState,
},
NotFound {
operation_id: SupervisorRotationOperationId,
},
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeHardCancelPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_member: BridgeMemberIncarnation,
pub operation_id: meerkat_core::ops::OperationId,
pub expected_run_id: meerkat_core::RunId,
pub reason: String,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeTrackedInputCancelPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_member: BridgeMemberIncarnation,
pub input_id: String,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "outcome", rename_all = "snake_case", deny_unknown_fields)]
#[non_exhaustive]
pub enum BridgeTrackedInputCancelOutcome {
NoEffect,
Cancelled,
Terminal { record: BridgeTurnOutcomeRecord },
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeTrackedInputCancelResponse {
pub expected_member: BridgeMemberIncarnation,
pub input_id: String,
pub outcome: BridgeTrackedInputCancelOutcome,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeInterruptPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expected_member: Option<BridgeMemberIncarnation>,
}
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(transparent)]
pub struct BridgeBootstrapToken(String);
impl BridgeBootstrapToken {
pub fn new(token: impl Into<String>) -> Self {
Self(token.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_string(self) -> String {
self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl From<String> for BridgeBootstrapToken {
fn from(token: String) -> Self {
Self(token)
}
}
impl From<&str> for BridgeBootstrapToken {
fn from(token: &str) -> Self {
Self(token.to_string())
}
}
impl std::fmt::Debug for BridgeBootstrapToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0.is_empty() {
write!(f, "BridgeBootstrapToken(empty)")
} else {
write!(f, "BridgeBootstrapToken(<redacted, {}B>)", self.0.len())
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeBindPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub expected_peer_id: String,
pub expected_address: String,
pub bootstrap_token: BridgeBootstrapToken,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeCapabilities {
#[serde(default = "supervisor_bridge_current_protocol_version")]
pub current_protocol_version: BridgeProtocolVersion,
#[serde(default = "supervisor_bridge_default_protocol_version")]
pub default_protocol_version: BridgeProtocolVersion,
#[serde(default = "default_supported_protocol_versions")]
pub supported_protocol_versions: Vec<BridgeProtocolVersion>,
#[serde(default)]
pub deliver_member_input: bool,
#[serde(default)]
pub observe_member: bool,
#[serde(default)]
pub interrupt_member: bool,
#[serde(default)]
pub hard_cancel_member: bool,
#[serde(default, skip_serializing_if = "bool_is_false")]
pub tracked_input_cancel: bool,
#[serde(default)]
pub retire_member: bool,
#[serde(default)]
pub destroy_member: bool,
#[serde(default)]
pub wire_member: bool,
#[serde(default)]
pub unwire_member: bool,
#[serde(default, skip_serializing_if = "bool_is_false")]
pub durable_sessions: bool,
#[serde(default, skip_serializing_if = "bool_is_false")]
pub autonomous_members: bool,
#[serde(default, skip_serializing_if = "bool_is_false")]
pub memory_store: bool,
#[serde(default, skip_serializing_if = "bool_is_false")]
pub mcp: bool,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub engine_version: String,
#[serde(default, skip_serializing_if = "bool_is_false")]
pub approval_forwarding: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub resolvable_providers: Vec<meerkat_core::Provider>,
}
impl Default for BridgeCapabilities {
fn default() -> Self {
Self {
current_protocol_version: supervisor_bridge_current_protocol_version(),
default_protocol_version: supervisor_bridge_default_protocol_version(),
supported_protocol_versions: supervisor_bridge_supported_protocol_versions().to_vec(),
deliver_member_input: false,
observe_member: false,
interrupt_member: false,
hard_cancel_member: false,
tracked_input_cancel: false,
retire_member: false,
destroy_member: false,
wire_member: false,
unwire_member: false,
durable_sessions: false,
autonomous_members: false,
memory_store: false,
mcp: false,
engine_version: String::new(),
approval_forwarding: false,
resolvable_providers: Vec::new(),
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeBindResponse {
pub peer_id: String,
pub address: String,
pub capabilities: BridgeCapabilities,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeAck {
pub ok: bool,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeDeliveryPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub input_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transcript_interaction_id: Option<String>,
pub content: meerkat_core::types::ContentInput,
pub handling_mode: meerkat_core::types::HandlingMode,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub objective_id: Option<meerkat_core::interaction::ObjectiveId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expected_member: Option<BridgeMemberIncarnation>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub injected_context: Vec<meerkat_core::types::ContentInput>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub turn: Option<BridgeTurnDirective>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_tracking: Option<BridgeOutcomeTracking>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum BridgeOutcomeTracking {
Interaction,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "outcome", rename_all = "snake_case", deny_unknown_fields)]
pub enum BridgeDeliveryOutcome {
Accepted,
Deduplicated {
existing_input_id: String,
},
Rejected {
cause: BridgeDeliveryRejectionCause,
reason: String,
},
}
pub const BRIDGE_TURN_OUTCOME_ACK_MAX: usize = 64;
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
#[non_exhaustive]
pub enum BridgeDeliveryRejectionCause {
NotReady { state: BridgeMemberRuntimeState },
DurabilityViolation { detail: String },
PeerHandlingModeInvalid { detail: String },
Internal { detail: String },
TurnDirectiveUnsupported { detail: String },
OutcomeJournalFull { retained: u32, limit: u32 },
StaleMemberIncarnation { current: BridgeMemberIncarnation },
StaleMemberResidency {
expected: BridgeMemberIncarnation,
#[serde(default, skip_serializing_if = "Option::is_none")]
current: Option<BridgeMemberIncarnation>,
},
}
impl std::fmt::Display for BridgeDeliveryRejectionCause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotReady { state } => write!(f, "not_ready(state={state})"),
Self::DurabilityViolation { detail } => {
write!(f, "durability_violation(detail={detail})")
}
Self::PeerHandlingModeInvalid { detail } => {
write!(f, "peer_handling_mode_invalid(detail={detail})")
}
Self::TurnDirectiveUnsupported { detail } => {
write!(f, "turn_directive_unsupported(detail={detail})")
}
Self::OutcomeJournalFull { retained, limit } => {
write!(
f,
"outcome_journal_full(retained={retained}, limit={limit})"
)
}
Self::StaleMemberIncarnation { current } => write!(
f,
"stale_member_incarnation(current={}/{}/{}/{}/{}/{}/{})",
current.mob_id,
current.agent_identity,
current.host_id,
current.binding_generation,
current.member_session_id,
current.generation,
current.fence_token
),
Self::StaleMemberResidency { expected, current } => write!(
f,
"stale_member_residency(expected={}/{}/{}/{}/{}/{}/{}, current={current:?})",
expected.mob_id,
expected.agent_identity,
expected.host_id,
expected.binding_generation,
expected.member_session_id,
expected.generation,
expected.fence_token
),
Self::Internal { detail } => write!(f, "internal(detail={detail})"),
}
}
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeDeliveryResponse {
pub input_id: String,
pub canonical_input_id: Option<String>,
pub outcome: BridgeDeliveryOutcome,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeMobPeerOverlayHandoff {
pub recipient_peer_id: String,
pub topology_epoch: u64,
pub peer_specs: Vec<BridgePeerSpec>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgePeerWiringPayload {
pub supervisor: BridgePeerSpec,
pub epoch: u64,
pub protocol_version: BridgeProtocolVersion,
pub peer_spec: BridgePeerSpec,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mob_peer_overlay: Option<BridgeMobPeerOverlayHandoff>,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeRetireResponse {
pub inputs_abandoned: usize,
pub inputs_pending_drain: usize,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeDestroyResponse {
pub inputs_abandoned: usize,
}
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct BridgeObservationResponse {
pub state: BridgeMemberRuntimeState,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub accepting_inputs: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub current_run_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub peer_connectivity: Option<BridgePeerConnectivity>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_error: Option<String>,
pub observed_at: String,
}
impl BridgeObservationResponse {
pub fn new(
state: BridgeMemberRuntimeState,
accepting_inputs: Option<bool>,
current_run_id: Option<String>,
peer_connectivity: Option<BridgePeerConnectivity>,
last_error: Option<String>,
observed_at: String,
) -> Self {
Self {
state,
accepting_inputs,
current_run_id,
peer_connectivity,
last_error,
observed_at,
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn observation_response_new_sets_observation_fields() {
let response = BridgeObservationResponse::new(
BridgeMemberRuntimeState::Running,
Some(true),
Some("run-1".to_string()),
Some(BridgePeerConnectivity::Reachable),
None,
"2026-04-16T07:00:00Z".to_string(),
);
assert_eq!(response.state, BridgeMemberRuntimeState::Running);
assert_eq!(response.current_run_id.as_deref(), Some("run-1"));
assert_eq!(response.accepting_inputs, Some(true));
assert_eq!(
response.peer_connectivity,
Some(BridgePeerConnectivity::Reachable)
);
}
fn sample_peer_spec() -> BridgePeerSpec {
BridgePeerSpec {
name: "member-a".to_string(),
peer_id: "peer-abc".to_string(),
address: "tcp://127.0.0.1:7000".to_string(),
pubkey: [0u8; 32],
}
}
fn sample_supervisor_payload() -> BridgeSupervisorPayload {
BridgeSupervisorPayload {
supervisor: sample_peer_spec(),
epoch: 42,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
}
}
fn sample_member_incarnation() -> BridgeMemberIncarnation {
BridgeMemberIncarnation {
mob_id: "mob-1".to_string(),
agent_identity: "worker-1".to_string(),
host_id: "host-1".to_string(),
binding_generation: 1,
member_session_id: "session-1".to_string(),
generation: 1,
fence_token: 3,
}
}
fn sample_hard_cancel_payload() -> BridgeHardCancelPayload {
BridgeHardCancelPayload {
supervisor: sample_peer_spec(),
epoch: 42,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
expected_member: sample_member_incarnation(),
operation_id: meerkat_core::ops::OperationId(uuid::Uuid::from_u128(0xCA11)),
expected_run_id: meerkat_core::RunId::from_uuid(uuid::Uuid::from_u128(0xA11CE)),
reason: "test hard cancel".to_string(),
}
}
fn sample_tracked_input_cancel_payload() -> BridgeTrackedInputCancelPayload {
BridgeTrackedInputCancelPayload {
supervisor: sample_peer_spec(),
epoch: 42,
protocol_version: BridgeProtocolVersion::V4,
expected_member: sample_member_incarnation(),
input_id: uuid::Uuid::from_u128(0xCA11CE1).to_string(),
}
}
fn sample_interrupt_payload() -> BridgeInterruptPayload {
BridgeInterruptPayload {
supervisor: sample_peer_spec(),
epoch: 42,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
expected_member: None,
}
}
fn sample_wiring_payload() -> BridgePeerWiringPayload {
let peer_spec = BridgePeerSpec {
name: "member-b".to_string(),
peer_id: "peer-xyz".to_string(),
address: "tcp://127.0.0.1:7001".to_string(),
pubkey: [0u8; 32],
};
BridgePeerWiringPayload {
supervisor: sample_peer_spec(),
epoch: 7,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
mob_peer_overlay: Some(BridgeMobPeerOverlayHandoff {
recipient_peer_id: sample_peer_spec().peer_id,
topology_epoch: 11,
peer_specs: vec![peer_spec.clone()],
}),
peer_spec,
}
}
fn valid_trusted_peer(name: &str, seed: u8, address: &str) -> TrustedPeerDescriptor {
let pubkey = [seed; 32];
TrustedPeerDescriptor::unsigned_with_pubkey(
name.to_string(),
PeerId::from_ed25519_pubkey(&pubkey).as_str(),
pubkey,
address,
)
.expect("valid trusted peer descriptor")
}
fn sample_supervisor_rotation_operation_id() -> SupervisorRotationOperationId {
"87de75b9-9bce-4c28-a022-7de5c9d7d480"
.parse()
.expect("valid supervisor rotation operation id")
}
fn sample_supervisor_rotation_target() -> BridgePeerSpec {
valid_trusted_peer("next-supervisor", 9, "tcp://127.0.0.1:7010").into()
}
fn sample_supervisor_rotation_submit() -> BridgeSupervisorRotationSubmit {
BridgeSupervisorRotationSubmit {
operation_id: sample_supervisor_rotation_operation_id(),
target: sample_supervisor_rotation_target(),
target_epoch: 43,
protocol_version: BridgeProtocolVersion::V4,
}
}
fn sample_supervisor_rotation_receipt() -> BridgeSupervisorRotationOperationReceipt {
BridgeSupervisorRotationOperationReceipt {
operation_id: sample_supervisor_rotation_operation_id(),
target: BridgeSupervisorRotationTargetReceipt {
target: sample_supervisor_rotation_target(),
target_epoch: 43,
},
}
}
#[test]
fn supervisor_rotation_operation_id_validates_uuid_at_json_ingress() {
let operation_id = sample_supervisor_rotation_operation_id();
let value = serde_json::to_value(operation_id).expect("serialize operation id");
assert_eq!(value, json!("87de75b9-9bce-4c28-a022-7de5c9d7d480"));
let decoded: SupervisorRotationOperationId =
serde_json::from_value(value).expect("decode operation id");
assert_eq!(decoded, operation_id);
assert!(
serde_json::from_value::<SupervisorRotationOperationId>(json!("not-a-uuid")).is_err(),
"an unchecked string must not cross the operation-id boundary"
);
}
#[test]
fn host_runtime_incarnation_validates_uuid_at_json_ingress() {
let incarnation: BridgeHostRuntimeIncarnation = "11111111-2222-4333-8444-555555555555"
.parse()
.expect("valid host runtime incarnation");
let value = serde_json::to_value(incarnation).expect("serialize host incarnation");
assert_eq!(value, json!("11111111-2222-4333-8444-555555555555"));
let decoded: BridgeHostRuntimeIncarnation =
serde_json::from_value(value).expect("decode host incarnation");
assert_eq!(decoded, incarnation);
assert!(
serde_json::from_value::<BridgeHostRuntimeIncarnation>(json!("not-a-uuid")).is_err(),
"an unchecked string must not cross the host-incarnation boundary"
);
}
#[test]
fn supervisor_rotation_submit_uses_closed_one_way_delivery_envelope() {
let delivery =
BridgeSupervisorDelivery::SubmitSupervisorRotation(sample_supervisor_rotation_submit());
assert_eq!(delivery.protocol_version(), BridgeProtocolVersion::V4);
let value = serde_json::to_value(&delivery).expect("serialize delivery");
assert_eq!(value["delivery"], json!("submit_supervisor_rotation"));
assert_eq!(
value["operation_id"],
json!("87de75b9-9bce-4c28-a022-7de5c9d7d480")
);
assert_eq!(value["target_epoch"], json!(43));
assert_eq!(value["protocol_version"], json!(4));
let decoded: BridgeSupervisorDelivery =
serde_json::from_value(value).expect("decode delivery");
assert_eq!(decoded, delivery);
}
#[test]
fn supervisor_rotation_delivery_does_not_admit_request_commands_or_unknown_fields() {
let observe = BridgeCommand::ObserveSupervisorRotation(BridgeSupervisorRotationObserve {
operation_id: sample_supervisor_rotation_operation_id(),
observer: sample_peer_spec(),
observer_epoch: 42,
protocol_version: BridgeProtocolVersion::V4,
});
let observe_value = serde_json::to_value(observe).expect("serialize observe command");
assert!(
serde_json::from_value::<BridgeSupervisorDelivery>(observe_value).is_err(),
"a request command must never decode as a one-way supervisor delivery"
);
let mut delivery_value = serde_json::to_value(
BridgeSupervisorDelivery::SubmitSupervisorRotation(sample_supervisor_rotation_submit()),
)
.expect("serialize delivery");
delivery_value
.as_object_mut()
.expect("delivery object")
.insert("user_content".to_string(), json!(true));
assert!(
serde_json::from_value::<BridgeSupervisorDelivery>(delivery_value).is_err(),
"the delivery envelope must fail closed on arbitrary user fields"
);
}
#[test]
fn supervisor_rotation_observe_is_v4_request_command() {
let command = BridgeCommand::ObserveSupervisorRotation(BridgeSupervisorRotationObserve {
operation_id: sample_supervisor_rotation_operation_id(),
observer: sample_peer_spec(),
observer_epoch: 42,
protocol_version: BridgeProtocolVersion::V4,
});
assert_eq!(command.protocol_version(), BridgeProtocolVersion::V4);
assert_command_round_trip(&command);
let value = serde_json::to_value(command).expect("serialize observe command");
assert_eq!(value["command"], json!("observe_supervisor_rotation"));
assert_eq!(value["observer_epoch"], json!(42));
assert_eq!(value["protocol_version"], json!(4));
}
#[test]
fn supervisor_rotation_pending_and_not_found_are_observation_only_outcomes() {
let pending = BridgeReply::SupervisorRotation(BridgeSupervisorRotationObservation::Found {
state: BridgeSupervisorRotationState::Pending {
operation: sample_supervisor_rotation_receipt(),
phase: BridgeSupervisorRotationPendingPhase::PreviousRevokePending,
},
});
let pending_value = serde_json::to_value(&pending).expect("serialize pending reply");
assert_eq!(pending_value["result"], json!("supervisor_rotation"));
assert_eq!(pending_value["outcome"], json!("found"));
assert_eq!(pending_value["state"]["status"], json!("pending"));
assert_eq!(
pending_value["state"]["operation"]["operation_id"],
json!("87de75b9-9bce-4c28-a022-7de5c9d7d480")
);
assert_eq!(
pending_value["state"]["phase"],
json!("previous_revoke_pending")
);
let pending_round_trip: BridgeReply =
serde_json::from_value(pending_value).expect("decode pending reply");
assert_eq!(pending_round_trip, pending);
let not_found =
BridgeReply::SupervisorRotation(BridgeSupervisorRotationObservation::NotFound {
operation_id: sample_supervisor_rotation_operation_id(),
});
let not_found_value = serde_json::to_value(¬_found).expect("serialize not-found reply");
assert_eq!(not_found_value["outcome"], json!("not_found"));
assert!(not_found_value.get("receipt").is_none());
let not_found_round_trip: BridgeReply =
serde_json::from_value(not_found_value).expect("decode not-found reply");
assert_eq!(not_found_round_trip, not_found);
}
#[test]
fn supervisor_rotation_terminal_receipts_pin_operation_and_exact_target() {
let completed_receipt = sample_supervisor_rotation_receipt();
let completed =
BridgeReply::SupervisorRotation(BridgeSupervisorRotationObservation::Found {
state: BridgeSupervisorRotationState::Completed {
receipt: completed_receipt.clone(),
},
});
let completed_value = serde_json::to_value(&completed).expect("serialize completed reply");
let receipt = &completed_value["state"]["receipt"];
assert_eq!(
receipt["operation_id"],
json!("87de75b9-9bce-4c28-a022-7de5c9d7d480")
);
assert_eq!(receipt["target"]["target_epoch"], json!(43));
assert_eq!(
receipt["target"]["target"]["peer_id"],
json!(sample_supervisor_rotation_target().peer_id)
);
let completed_round_trip: BridgeReply =
serde_json::from_value(completed_value).expect("decode completed reply");
assert_eq!(completed_round_trip, completed);
let rejected =
BridgeReply::SupervisorRotation(BridgeSupervisorRotationObservation::Found {
state: BridgeSupervisorRotationState::Rejected {
receipt: BridgeSupervisorRotationRejectionReceipt {
operation: completed_receipt,
cause: BridgeSupervisorRotationRejectionCause::OperationConflict,
reason: "operation id is already bound to another target".to_string(),
},
},
});
let rejected_value = serde_json::to_value(&rejected).expect("serialize rejected reply");
assert_eq!(
rejected_value["state"]["receipt"]["cause"],
json!("operation_conflict")
);
assert_eq!(
rejected_value["state"]["receipt"]["reason"],
json!("operation id is already bound to another target")
);
let rejected_round_trip: BridgeReply =
serde_json::from_value(rejected_value).expect("decode rejected reply");
assert_eq!(rejected_round_trip, rejected);
}
fn assert_command_round_trip(cmd: &BridgeCommand) {
let value = serde_json::to_value(cmd).expect("serialize command");
let decoded: BridgeCommand = serde_json::from_value(value.clone()).expect("decode command");
let reencoded = serde_json::to_value(&decoded).expect("reserialize command");
assert_eq!(
value, reencoded,
"BridgeCommand round-trip must preserve wire shape"
);
}
#[test]
fn bridge_peer_wiring_payload_carries_generated_mob_overlay_handoff() {
let peer = valid_trusted_peer("member-b", 2, "tcp://127.0.0.1:7001");
let external = valid_trusted_peer("external", 3, "tcp://127.0.0.1:7002");
let payload = BridgePeerWiringPayload {
supervisor: sample_peer_spec(),
epoch: 7,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
peer_spec: peer.clone().into(),
mob_peer_overlay: Some(BridgeMobPeerOverlayHandoff {
recipient_peer_id: "recipient-peer".to_string(),
topology_epoch: 13,
peer_specs: vec![peer.into(), external.into()],
}),
};
let value = serde_json::to_value(&payload).expect("serialize payload");
assert_eq!(value["mob_peer_overlay"]["topology_epoch"], json!(13));
assert_eq!(
value["mob_peer_overlay"]["recipient_peer_id"],
json!("recipient-peer")
);
assert_eq!(
value["mob_peer_overlay"]["peer_specs"]
.as_array()
.expect("overlay array")
.len(),
2
);
let decoded: BridgePeerWiringPayload =
serde_json::from_value(value).expect("decode payload");
let decoded_overlay = decoded
.mob_peer_overlay
.expect("V3 payload carries the overlay");
assert_eq!(decoded_overlay.topology_epoch, 13);
assert_eq!(decoded_overlay.peer_specs.len(), 2);
}
#[test]
fn wire_member_v2_payload_without_overlay_decodes_as_none() {
let mut value = serde_json::to_value(BridgeCommand::WireMember(sample_wiring_payload()))
.expect("serialize");
let obj = value.as_object_mut().expect("command object");
obj.remove("mob_peer_overlay");
obj.insert("protocol_version".to_string(), json!(2));
match decode_bridge_command(value).expect("V2 wiring payload must decode on a V3 receiver")
{
BridgeCommand::WireMember(payload) => {
assert_eq!(payload.protocol_version, BridgeProtocolVersion::V2);
assert!(
payload.mob_peer_overlay.is_none(),
"a V2 wiring payload carries no overlay"
);
}
other => panic!("expected WireMember, got {other:?}"),
}
}
#[test]
fn wire_member_v3_payload_round_trips_with_overlay() {
let value = serde_json::to_value(BridgeCommand::WireMember(sample_wiring_payload()))
.expect("serialize");
assert_eq!(
value["protocol_version"],
json!(4),
"the sample stamps CURRENT (V4); wiring itself only demands V3"
);
assert!(
!value["mob_peer_overlay"].is_null(),
"a V3 wiring payload always carries the overlay"
);
match decode_bridge_command(value).expect("V3 wiring payload decodes") {
BridgeCommand::WireMember(payload) => {
assert!(payload.mob_peer_overlay.is_some());
}
other => panic!("expected WireMember, got {other:?}"),
}
}
#[test]
fn wiring_command_with_unsupported_protocol_version_rejects_before_serde() {
let mut value = serde_json::to_value(BridgeCommand::WireMember(sample_wiring_payload()))
.expect("serialize");
value
.as_object_mut()
.expect("command object")
.insert("protocol_version".to_string(), json!(99));
let err =
decode_bridge_command(value).expect_err("unsupported protocol version must reject");
assert!(
matches!(err, BridgeCommandDecodeError::UnsupportedProtocolVersion(_)),
"expected typed UnsupportedProtocolVersion, got {err:?}"
);
}
#[test]
fn bridge_command_unknown_top_level_field_fails_closed() {
let mut value =
serde_json::to_value(BridgeCommand::ObserveMember(sample_supervisor_payload()))
.expect("serialize command");
value["extra_behavior"] = json!(true);
let err = serde_json::from_value::<BridgeCommand>(value)
.expect_err("unknown command fields must fail at serde boundary");
let message = err.to_string();
assert!(
message.contains("extra_behavior") || message.contains("unknown field"),
"expected unknown field error, got: {message}"
);
}
#[test]
fn bridge_command_unknown_nested_payload_field_fails_closed() {
let mut value =
serde_json::to_value(BridgeCommand::ObserveMember(sample_supervisor_payload()))
.expect("serialize command");
value["supervisor"]["extra_behavior"] = json!(true);
let err = serde_json::from_value::<BridgeCommand>(value)
.expect_err("unknown nested payload fields must fail at serde boundary");
let message = err.to_string();
assert!(
message.contains("extra_behavior") || message.contains("unknown field"),
"expected unknown field error, got: {message}"
);
}
#[test]
fn bridge_command_bind_member_round_trip() {
let cmd = BridgeCommand::BindMember(BridgeBindPayload {
supervisor: sample_peer_spec(),
epoch: 1,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
expected_peer_id: "peer-expected".to_string(),
expected_address: "tcp://127.0.0.1:9000".to_string(),
bootstrap_token: "bootstrap-secret".into(),
});
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_peer_spec_rejects_unknown_address_scheme() {
let spec = BridgePeerSpec {
name: "member-a".to_string(),
peer_id: "aaaaaaaa-0000-4000-8000-000000000001".to_string(),
address: "http://127.0.0.1:7000".to_string(),
pubkey: [0u8; 32],
};
let err = meerkat_core::comms::TrustedPeerDescriptor::try_from(&spec)
.expect_err("supervisor bridge peer specs must fail closed on unknown schemes");
assert!(
err.contains("unknown peer address transport"),
"unexpected error: {err}",
);
}
#[test]
fn bridge_peer_spec_rejects_schemeless_address() {
let spec = BridgePeerSpec {
name: "member-a".to_string(),
peer_id: "aaaaaaaa-0000-4000-8000-000000000001".to_string(),
address: "127.0.0.1:7000".to_string(),
pubkey: [0u8; 32],
};
let err = meerkat_core::comms::TrustedPeerDescriptor::try_from(&spec)
.expect_err("supervisor bridge peer specs must fail closed on schemeless addresses");
assert!(
err.contains("missing transport scheme"),
"unexpected error: {err}",
);
}
#[test]
fn bridge_peer_spec_rejects_zero_pubkey() {
let spec = BridgePeerSpec {
name: "member-a".to_string(),
peer_id: PeerId::from_ed25519_pubkey(&[1u8; 32]).to_string(),
address: "tcp://127.0.0.1:7000".to_string(),
pubkey: [0u8; 32],
};
let err = meerkat_core::comms::TrustedPeerDescriptor::try_from(&spec)
.expect_err("supervisor bridge peer specs must fail closed on zero pubkeys");
assert!(
err.contains("pubkey") && err.contains("non-zero"),
"unexpected error: {err}",
);
}
#[test]
fn bridge_peer_spec_missing_pubkey_defaults_to_zero_and_rejects() {
let value = json!({
"name": "member-a",
"peer_id": PeerId::from_ed25519_pubkey(&[2u8; 32]).to_string(),
"address": "tcp://127.0.0.1:7000"
});
let spec: BridgePeerSpec =
serde_json::from_value(value).expect("legacy bridge peer spec should deserialize");
let err = meerkat_core::comms::TrustedPeerDescriptor::try_from(&spec)
.expect_err("missing pubkey must not become trusted zero-key authority");
assert!(
err.contains("pubkey") && err.contains("non-zero"),
"unexpected error: {err}",
);
}
#[test]
fn bridge_command_authorize_supervisor_round_trip() {
let cmd = BridgeCommand::AuthorizeSupervisor(sample_supervisor_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_revoke_supervisor_round_trip() {
let cmd = BridgeCommand::RevokeSupervisor(sample_supervisor_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_deliver_member_input_round_trip() {
let cmd = BridgeCommand::DeliverMemberInput(BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
input_id: "input-1".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("hello".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: None,
injected_context: Vec::new(),
turn: None,
outcome_tracking: None,
});
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_declare_member_outbound_taint_round_trip() {
for taint in [
None,
Some(meerkat_core::comms::SenderContentTaint::Clean),
Some(meerkat_core::comms::SenderContentTaint::Tainted),
] {
let cmd = BridgeCommand::DeclareMemberOutboundTaint(BridgeOutboundTaintPayload {
supervisor: sample_peer_spec(),
epoch: 4,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
target: Some(BridgeOutboundTaintTarget::PeerOnly),
taint,
});
assert_command_round_trip(&cmd);
assert_eq!(cmd.protocol_version(), SUPERVISOR_BRIDGE_PROTOCOL_VERSION);
}
}
#[test]
fn bridge_outbound_taint_v3_legacy_shape_decodes_but_target_extension_requires_v4() {
let legacy = BridgeCommand::DeclareMemberOutboundTaint(BridgeOutboundTaintPayload {
supervisor: sample_peer_spec(),
epoch: 4,
protocol_version: BridgeProtocolVersion::V3,
target: None,
taint: Some(meerkat_core::comms::SenderContentTaint::Tainted),
});
let legacy_value = serde_json::to_value(&legacy).expect("serialize legacy V3 taint");
assert!(
legacy_value.get("target").is_none(),
"legacy payload must preserve the pre-V4 omitted shape"
);
let decoded = decode_bridge_command(legacy_value.clone())
.expect("omitted-target V3 payload remains decodable");
assert_eq!(decoded, legacy);
let mut under_versioned = legacy_value;
under_versioned
.as_object_mut()
.expect("command object")
.insert("target".to_string(), serde_json::json!("peer_only"));
let error = decode_bridge_command(under_versioned)
.expect_err("the target-bearing shape was introduced in V4");
assert!(matches!(
error,
BridgeCommandDecodeError::UnsupportedProtocolVersion(_)
));
let v4 = BridgeCommand::DeclareMemberOutboundTaint(BridgeOutboundTaintPayload {
supervisor: sample_peer_spec(),
epoch: 4,
protocol_version: BridgeProtocolVersion::V4,
target: Some(BridgeOutboundTaintTarget::PeerOnly),
taint: None,
});
assert_eq!(
decode_bridge_command(serde_json::to_value(&v4).expect("serialize V4 taint"))
.expect("target-bearing V4 payload decodes"),
v4
);
}
#[test]
fn bridge_outbound_taint_payload_rejects_unknown_fields() {
let mut value = serde_json::to_value(BridgeOutboundTaintPayload {
supervisor: sample_peer_spec(),
epoch: 4,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
target: Some(BridgeOutboundTaintTarget::PeerOnly),
taint: Some(meerkat_core::comms::SenderContentTaint::Tainted),
})
.expect("serialize payload");
value
.as_object_mut()
.expect("payload object")
.insert("unexpected".to_string(), serde_json::json!(true));
let result: Result<BridgeOutboundTaintPayload, _> = serde_json::from_value(value);
assert!(
result.is_err(),
"unknown fields must fail loud at the wire boundary"
);
}
#[test]
fn bridge_command_deliver_member_input_with_injected_context_round_trip() {
let cmd = BridgeCommand::DeliverMemberInput(BridgeDeliveryPayload {
objective_id: Some(meerkat_core::interaction::ObjectiveId::new()),
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
input_id: "input-1".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("hello".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: None,
injected_context: vec![
meerkat_core::types::ContentInput::Text("ambient alpha".to_string()),
meerkat_core::types::ContentInput::Text("ambient beta".to_string()),
],
turn: None,
outcome_tracking: None,
});
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_delivery_interaction_tracking_is_absent_omitted_and_v4_gated() {
let mut payload = BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: BridgeProtocolVersion::V4,
input_id: "input-tracked".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("hello".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: None,
injected_context: Vec::new(),
turn: None,
outcome_tracking: None,
};
let absent = serde_json::to_value(&payload).expect("serialize absent tracking marker");
assert!(
absent.get("outcome_tracking").is_none(),
"absent marker must preserve the pre-field wire shape: {absent}"
);
payload.outcome_tracking = Some(BridgeOutcomeTracking::Interaction);
let command = BridgeCommand::DeliverMemberInput(payload.clone());
let value = serde_json::to_value(&command).expect("serialize tracked interaction");
assert_eq!(value["outcome_tracking"], json!("interaction"));
assert_eq!(
decode_bridge_command(value).expect("V4 tracking marker decodes"),
command
);
payload.protocol_version = BridgeProtocolVersion::V3;
let error = decode_bridge_command(
serde_json::to_value(BridgeCommand::DeliverMemberInput(payload))
.expect("serialize under-versioned tracked interaction"),
)
.expect_err("the tracking marker belongs to the V4 semantic fold");
assert!(matches!(
error,
BridgeCommandDecodeError::UnsupportedProtocolVersion(_)
));
}
#[test]
fn bridge_delivery_payload_empty_injected_context_is_byte_compatible() {
let payload = BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
input_id: "input-1".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("hello".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: None,
injected_context: Vec::new(),
turn: None,
outcome_tracking: None,
};
let value = serde_json::to_value(&payload).expect("serialize payload");
assert!(
value.get("injected_context").is_none(),
"empty injected_context must be omitted: {value}"
);
assert!(
value.get("turn").is_none(),
"absent turn directive must be omitted: {value}"
);
assert!(
value.get("outcome_tracking").is_none(),
"absent outcome tracking must be omitted: {value}"
);
assert!(
value.get("transcript_interaction_id").is_none(),
"absent transcript interaction id must preserve the legacy shape: {value}"
);
let mut pre_field = serde_json::to_value(&payload).expect("serialize payload");
pre_field
.as_object_mut()
.expect("payload object")
.remove("injected_context");
let parsed: BridgeDeliveryPayload =
serde_json::from_value(pre_field).expect("pre-field payload deserializes");
assert!(parsed.injected_context.is_empty());
assert!(parsed.transcript_interaction_id.is_none());
}
#[test]
fn bridge_delivery_transcript_identity_is_absent_compatible_and_v4_gated() {
let legacy = BridgeCommand::DeliverMemberInput(BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: BridgeProtocolVersion::V3,
input_id: "input-legacy".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("hello".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: None,
injected_context: Vec::new(),
turn: None,
outcome_tracking: None,
});
let mut value = serde_json::to_value(&legacy).expect("serialize legacy delivery");
assert_eq!(
decode_bridge_command(value.clone()).expect("omitted field remains decodable"),
legacy
);
value["transcript_interaction_id"] = json!(uuid::Uuid::from_u128(0x1234).to_string());
assert!(matches!(
decode_bridge_command(value)
.expect_err("the transcript identity carrier was introduced in V4"),
BridgeCommandDecodeError::UnsupportedProtocolVersion(_)
));
}
#[test]
fn bridge_command_observe_member_round_trip() {
let cmd = BridgeCommand::ObserveMember(sample_supervisor_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_interrupt_member_round_trip() {
let cmd = BridgeCommand::InterruptMember(sample_interrupt_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_hard_cancel_member_round_trip() {
let cmd = BridgeCommand::HardCancelMember(sample_hard_cancel_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_tracked_input_cancel_round_trip() {
let cmd = BridgeCommand::CancelTrackedMemberInput(sample_tracked_input_cancel_payload());
assert_command_round_trip(&cmd);
assert_eq!(cmd.protocol_version(), BridgeProtocolVersion::V4);
let reply = BridgeReply::TrackedInputCancelled(BridgeTrackedInputCancelResponse {
expected_member: sample_member_incarnation(),
input_id: sample_tracked_input_cancel_payload().input_id,
outcome: BridgeTrackedInputCancelOutcome::NoEffect,
});
let encoded = serde_json::to_value(&reply).expect("serialize tracked cancel reply");
assert_eq!(encoded["result"], json!("tracked_input_cancelled"));
let decoded: BridgeReply =
serde_json::from_value(encoded).expect("decode tracked cancel reply");
assert_eq!(decoded, reply);
}
#[test]
fn bridge_command_hard_cancel_requires_operation_and_expected_run_truth() {
let command = BridgeCommand::HardCancelMember(sample_hard_cancel_payload());
let value = serde_json::to_value(&command).expect("serialize hard-cancel command");
assert_eq!(
value.get("operation_id"),
Some(&json!(uuid::Uuid::from_u128(0xCA11).to_string())),
"hard cancel must expose one stable operation id on the wire"
);
assert_eq!(
value.get("expected_run_id"),
Some(&json!(uuid::Uuid::from_u128(0xA11CE).to_string())),
"hard cancel must expose the exact run fence on the wire"
);
for required in ["operation_id", "expected_run_id"] {
let mut missing = value.clone();
missing
.as_object_mut()
.expect("hard-cancel command object")
.remove(required);
let error = decode_bridge_command(missing)
.expect_err("missing hard-cancel identity truth must fail closed");
assert!(
error.to_string().contains("missing field"),
"missing {required} should be a strict decode failure: {error}"
);
}
}
#[test]
fn bridge_command_retire_member_round_trip() {
let cmd = BridgeCommand::RetireMember(sample_supervisor_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_destroy_member_round_trip() {
let cmd = BridgeCommand::DestroyMember(sample_supervisor_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_wire_member_round_trip() {
let cmd = BridgeCommand::WireMember(sample_wiring_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_command_unwire_member_round_trip() {
let cmd = BridgeCommand::UnwireMember(sample_wiring_payload());
assert_command_round_trip(&cmd);
}
#[test]
fn bridge_reply_rejected_round_trip_with_typed_cause() {
let reply = BridgeReply::Rejected {
cause: BridgeRejectionCause::StaleSupervisor,
reason: "epoch too low".to_string(),
};
let value = serde_json::to_value(&reply).expect("serialize rejected reply");
assert_eq!(
value,
json!({
"result": "rejected",
"cause": "stale_supervisor",
"reason": "epoch too low",
}),
"wire shape must tag rejection with `result` + `cause` + `reason`"
);
let decoded: BridgeReply = serde_json::from_value(value.clone()).expect("decode reply");
match &decoded {
BridgeReply::Rejected { cause, reason } => {
assert_eq!(*cause, BridgeRejectionCause::StaleSupervisor);
assert_eq!(reason, "epoch too low");
}
other => panic!("expected BridgeReply::Rejected, got {other:?}"),
}
let reencoded = serde_json::to_value(&decoded).expect("reserialize reply");
assert_eq!(value, reencoded);
}
#[test]
fn bridge_rejection_decoder_accepts_typed_protocol_v2_rejection() {
let value = json!({
"result": "rejected",
"cause": "sender_mismatch",
"reason": "wrong supervisor",
});
let decoded = decode_bridge_rejection_reply(SUPERVISOR_BRIDGE_PROTOCOL_VERSION, &value)
.expect("typed rejection should decode");
assert_eq!(
decoded.typed_cause(),
Some(BridgeRejectionCause::SenderMismatch)
);
assert_eq!(decoded.reason(), "wrong supervisor");
assert!(!decoded.is_legacy_v1_raw_string());
}
#[test]
fn bridge_rejection_decoder_rejects_raw_string_for_protocol_v2() {
let value = json!("legacy rejection");
assert!(
decode_bridge_rejection_reply(SUPERVISOR_BRIDGE_PROTOCOL_VERSION, &value).is_none(),
"protocol v2 must not promote raw strings into typed rejection causes"
);
}
#[test]
fn bridge_rejection_decoder_isolates_raw_string_to_legacy_v1() {
let value = json!("legacy rejection");
let decoded = decode_legacy_v1_raw_string_rejection(&value)
.expect("legacy raw string should decode only through the explicit v1 helper");
assert_eq!(decoded.typed_cause(), None);
assert_eq!(decoded.reason(), "legacy rejection");
assert!(decoded.is_legacy_v1_raw_string());
}
#[test]
fn bridge_command_reports_payload_protocol_version() {
let command = BridgeCommand::AuthorizeSupervisor(sample_supervisor_payload());
assert_eq!(
command.protocol_version(),
SUPERVISOR_BRIDGE_PROTOCOL_VERSION
);
}
#[test]
fn supervisor_bridge_protocol_versions_are_reported_from_single_authority() {
assert_eq!(
supervisor_bridge_current_protocol_version(),
SUPERVISOR_BRIDGE_PROTOCOL_VERSION
);
assert_eq!(
supervisor_bridge_default_protocol_version(),
SUPERVISOR_BRIDGE_DEFAULT_PROTOCOL_VERSION
);
assert_eq!(
supervisor_bridge_supported_protocol_versions(),
&[
BridgeProtocolVersion::V2,
BridgeProtocolVersion::V3,
BridgeProtocolVersion::V4
]
);
assert_eq!(
supervisor_bridge_current_protocol_version(),
BridgeProtocolVersion::V4
);
assert_eq!(
supervisor_bridge_default_protocol_version(),
BridgeProtocolVersion::V4
);
assert!(supervisor_bridge_protocol_version_supported(
BridgeProtocolVersion::V2
));
assert!(supervisor_bridge_protocol_version_supported(
BridgeProtocolVersion::V3
));
assert!(supervisor_bridge_protocol_version_supported(
SUPERVISOR_BRIDGE_PROTOCOL_VERSION
));
assert!(BridgeProtocolVersion::V4.supports_multi_host());
assert!(!BridgeProtocolVersion::V3.supports_multi_host());
assert!(BridgeProtocolVersion::from_supported_u32(1).is_err());
assert!(BridgeProtocolVersion::from_supported_u32(5).is_err());
assert!(BridgeProtocolVersion::from_supported_u32(999).is_err());
}
#[test]
fn bridge_capabilities_default_reports_canonical_protocol_versions() {
let capabilities = BridgeCapabilities::default();
assert_eq!(
capabilities.current_protocol_version,
SUPERVISOR_BRIDGE_PROTOCOL_VERSION
);
assert_eq!(
capabilities.default_protocol_version,
SUPERVISOR_BRIDGE_DEFAULT_PROTOCOL_VERSION
);
assert_eq!(
capabilities.supported_protocol_versions,
vec![
BridgeProtocolVersion::V2,
BridgeProtocolVersion::V3,
BridgeProtocolVersion::V4
]
);
assert!(!capabilities.durable_sessions);
assert!(!capabilities.autonomous_members);
assert!(!capabilities.memory_store);
assert!(!capabilities.mcp);
assert!(!capabilities.approval_forwarding);
assert!(capabilities.engine_version.is_empty());
assert!(capabilities.resolvable_providers.is_empty());
}
#[test]
fn bridge_capabilities_deserialize_legacy_without_protocol_report() {
let capabilities: BridgeCapabilities = serde_json::from_value(json!({
"deliver_member_input": true,
"observe_member": true,
"interrupt_member": true,
"retire_member": true,
"destroy_member": true,
"wire_member": true,
"unwire_member": true,
}))
.expect("legacy capability payload without protocol report should decode");
assert_eq!(
capabilities.current_protocol_version,
SUPERVISOR_BRIDGE_PROTOCOL_VERSION
);
assert_eq!(
capabilities.default_protocol_version,
SUPERVISOR_BRIDGE_DEFAULT_PROTOCOL_VERSION
);
assert_eq!(
capabilities.supported_protocol_versions,
vec![
BridgeProtocolVersion::V2,
BridgeProtocolVersion::V3,
BridgeProtocolVersion::V4
]
);
assert!(capabilities.deliver_member_input);
assert!(capabilities.observe_member);
assert!(capabilities.interrupt_member);
assert!(!capabilities.hard_cancel_member);
assert!(capabilities.retire_member);
assert!(capabilities.destroy_member);
assert!(capabilities.wire_member);
assert!(capabilities.unwire_member);
assert!(!capabilities.durable_sessions);
assert!(!capabilities.autonomous_members);
assert!(!capabilities.memory_store);
assert!(!capabilities.mcp);
assert!(!capabilities.approval_forwarding);
assert!(capabilities.engine_version.is_empty());
assert!(capabilities.resolvable_providers.is_empty());
}
#[test]
fn bridge_bind_payload_rejects_unsupported_protocol_version_at_wire_boundary() {
let raw = json!({
"supervisor": {
"name": "mob/__mob_supervisor__",
"peer_id": "00000000-0000-0000-0000-00000000bbbb",
"address": "inproc://mob/__mob_supervisor__",
},
"epoch": 7,
"protocol_version": 999,
"expected_peer_id": "00000000-0000-0000-0000-00000000aaaa",
"expected_address": "inproc://member",
"bootstrap_token": "tok-raw-string",
});
let error = serde_json::from_value::<BridgeBindPayload>(raw)
.expect_err("unsupported protocol versions must fail closed at decode");
assert!(
error
.to_string()
.contains("unsupported supervisor bridge protocol version"),
"unexpected error: {error}",
);
}
#[test]
fn bridge_capabilities_reject_unsupported_protocol_versions_at_wire_boundary() {
let raw = json!({
"current_protocol_version": SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
"default_protocol_version": 999,
"supported_protocol_versions": [SUPERVISOR_BRIDGE_PROTOCOL_VERSION],
"deliver_member_input": true,
});
let error = serde_json::from_value::<BridgeCapabilities>(raw)
.expect_err("unsupported advertised defaults must fail closed at decode");
assert!(
error
.to_string()
.contains("unsupported supervisor bridge protocol version"),
"unexpected error: {error}",
);
}
#[test]
fn observation_response_round_trip_all_optional_present() {
let response = BridgeObservationResponse {
state: BridgeMemberRuntimeState::Running,
accepting_inputs: Some(true),
current_run_id: Some("run-42".to_string()),
peer_connectivity: Some(BridgePeerConnectivity::Reachable),
last_error: Some("transient network blip".to_string()),
observed_at: "2026-04-16T07:00:00Z".to_string(),
};
let value = serde_json::to_value(&response).expect("serialize observation");
assert_eq!(
value,
json!({
"state": "running",
"accepting_inputs": true,
"current_run_id": "run-42",
"peer_connectivity": "reachable",
"last_error": "transient network blip",
"observed_at": "2026-04-16T07:00:00Z",
})
);
let decoded: BridgeObservationResponse =
serde_json::from_value(value.clone()).expect("decode observation");
assert_eq!(decoded, response);
let reencoded = serde_json::to_value(&decoded).expect("reserialize observation");
assert_eq!(value, reencoded);
}
#[test]
fn observation_response_round_trip_all_optional_absent() {
let response = BridgeObservationResponse {
state: BridgeMemberRuntimeState::Idle,
accepting_inputs: None,
current_run_id: None,
peer_connectivity: None,
last_error: None,
observed_at: "2026-04-16T07:01:00Z".to_string(),
};
let value = serde_json::to_value(&response).expect("serialize observation");
assert_eq!(
value,
json!({
"state": "idle",
"observed_at": "2026-04-16T07:01:00Z",
}),
"absent optional fields must be skipped on the wire"
);
let decoded: BridgeObservationResponse =
serde_json::from_value(value.clone()).expect("decode observation");
assert_eq!(decoded, response);
let reencoded = serde_json::to_value(&decoded).expect("reserialize observation");
assert_eq!(value, reencoded);
}
#[test]
fn peer_connectivity_serializes_as_snake_case() {
for (variant, expected) in [
(BridgePeerConnectivity::Reachable, "reachable"),
(BridgePeerConnectivity::Unreachable, "unreachable"),
(BridgePeerConnectivity::Unknown, "unknown"),
] {
let value = serde_json::to_value(variant).expect("serialize connectivity");
assert_eq!(
value,
json!(expected),
"variant {variant:?} must serialize as {expected:?}"
);
let decoded: BridgePeerConnectivity =
serde_json::from_value(value).expect("decode connectivity");
assert_eq!(decoded, variant);
}
}
#[test]
fn member_runtime_state_display_and_round_trip_all_variants() {
let cases: &[(BridgeMemberRuntimeState, &str)] = &[
(BridgeMemberRuntimeState::Initializing, "initializing"),
(BridgeMemberRuntimeState::Idle, "idle"),
(BridgeMemberRuntimeState::Attached, "attached"),
(BridgeMemberRuntimeState::Running, "running"),
(BridgeMemberRuntimeState::Retired, "retired"),
(BridgeMemberRuntimeState::Stopped, "stopped"),
(BridgeMemberRuntimeState::Destroyed, "destroyed"),
];
for (variant, expected) in cases {
assert_eq!(
variant.to_string(),
*expected,
"Display output must match snake_case wire form for {variant:?}"
);
let value = serde_json::to_value(variant).expect("serialize runtime state");
assert_eq!(value, json!(expected));
let decoded: BridgeMemberRuntimeState =
serde_json::from_value(value).expect("decode runtime state");
assert_eq!(decoded, *variant);
}
}
#[test]
fn bridge_rejection_cause_snake_case_round_trip_all_unit_variants() {
let cases: &[(BridgeRejectionCause, &str)] = &[
(BridgeRejectionCause::NotBound, "not_bound"),
(BridgeRejectionCause::StaleSupervisor, "stale_supervisor"),
(BridgeRejectionCause::SenderMismatch, "sender_mismatch"),
(BridgeRejectionCause::AlreadyBound, "already_bound"),
(
BridgeRejectionCause::InvalidBootstrapToken,
"invalid_bootstrap_token",
),
(
BridgeRejectionCause::UnsupportedProtocolVersion,
"unsupported_protocol_version",
),
(
BridgeRejectionCause::InvalidSupervisorSpec,
"invalid_supervisor_spec",
),
(BridgeRejectionCause::InvalidPeerSpec, "invalid_peer_spec"),
(BridgeRejectionCause::AddressMismatch, "address_mismatch"),
(BridgeRejectionCause::Unsupported, "unsupported"),
(BridgeRejectionCause::Internal, "internal"),
(BridgeRejectionCause::StaleFence, "stale_fence"),
(BridgeRejectionCause::Unavailable, "unavailable"),
(
BridgeRejectionCause::SpecDigestMismatch,
"spec_digest_mismatch",
),
(
BridgeRejectionCause::RealmBackendUnavailable,
"realm_backend_unavailable",
),
(
BridgeRejectionCause::LiveTransportUnavailable,
"live_transport_unavailable",
),
(
BridgeRejectionCause::LiveChannelAlreadyBound,
"live_channel_already_bound",
),
(
BridgeRejectionCause::LiveChannelNotFound,
"live_channel_not_found",
),
(
BridgeRejectionCause::ResumeSessionNotFound,
"resume_session_not_found",
),
(
BridgeRejectionCause::LaunchModeUnsupported,
"launch_mode_unsupported",
),
(
BridgeRejectionCause::LaunchModePlacementMismatch,
"launch_mode_placement_mismatch",
),
];
for (cause, expected) in cases {
let value = serde_json::to_value(cause).expect("serialize cause");
assert_eq!(
value,
json!(expected),
"cause {cause:?} must serialize as {expected:?}"
);
let decoded: BridgeRejectionCause =
serde_json::from_value(value).expect("decode cause");
assert_eq!(&decoded, cause);
}
}
#[test]
fn bridge_rejection_cause_data_variants_round_trip_with_typed_detail() {
use crate::wire::mob::WireControlScope;
let cases: Vec<(BridgeRejectionCause, serde_json::Value)> = vec![
(
BridgeRejectionCause::StaleCursor {
watermark: 42,
generation: 3,
},
json!({"stale_cursor": {"watermark": 42, "generation": 3}}),
),
(
BridgeRejectionCause::OversizedEvent {
generation: 3,
durable_seq: 43,
next_seq: 44,
encoded_bytes: 600_000,
max_bytes: 524_288,
},
json!({"oversized_event": {
"generation": 3,
"durable_seq": 43,
"next_seq": 44,
"encoded_bytes": 600_000,
"max_bytes": 524_288,
}}),
),
(
BridgeRejectionCause::HistoryRowTooLarge {
index: 9,
encoded_bytes: 600_000,
max_bytes: 524_288,
},
json!({"history_row_too_large": {
"index": 9,
"encoded_bytes": 600_000,
"max_bytes": 524_288,
}}),
),
(
BridgeRejectionCause::SessionOwnershipConflict {
session_id: "session-1".to_string(),
},
json!({"session_ownership_conflict": {
"session_id": "session-1",
}}),
),
(
BridgeRejectionCause::ScopeDenied {
required: WireControlScope::Live,
presented: vec![WireControlScope::List, WireControlScope::ReadHistory],
},
json!({"scope_denied": {
"required": "live",
"presented": ["list", "read_history"],
}}),
),
(
BridgeRejectionCause::MaterializeBuildRejected {
cause: MemberBuildRejection::UnknownProviderForModel {
model: "mystery-model".to_string(),
},
},
json!({"materialize_build_rejected": {
"cause": {"unknown_provider_for_model": {"model": "mystery-model"}},
}}),
),
(
BridgeRejectionCause::MaterializeBuildRejected {
cause: MemberBuildRejection::BindingUnresolvable {
kind: ConnectionTargetErrorKind::UnknownRealm,
},
},
json!({"materialize_build_rejected": {
"cause": {"binding_unresolvable": {"kind": "unknown_realm"}},
}}),
),
(
BridgeRejectionCause::ModelUnresolvable {
model: "m1".to_string(),
},
json!({"model_unresolvable": {"model": "m1"}}),
),
(
BridgeRejectionCause::AuthBindingUnresolvable {
realm: "dev".to_string(),
binding: "default_anthropic".to_string(),
},
json!({"auth_binding_unresolvable": {"realm": "dev", "binding": "default_anthropic"}}),
),
(
BridgeRejectionCause::McpCommandMissing {
server: "docs".to_string(),
},
json!({"mcp_command_missing": {"server": "docs"}}),
),
(
BridgeRejectionCause::EnvKeyMissing {
key: "DOCS_TOKEN".to_string(),
},
json!({"env_key_missing": {"key": "DOCS_TOKEN"}}),
),
(
BridgeRejectionCause::HostEngineVersionChanged {
bound: "0.7.22".to_string(),
reported: "0.8.0".to_string(),
},
json!({"host_engine_version_changed": {"bound": "0.7.22", "reported": "0.8.0"}}),
),
(
BridgeRejectionCause::ModelNotRealtime {
model: "m1".to_string(),
provider: "anthropic".to_string(),
},
json!({"model_not_realtime": {"model": "m1", "provider": "anthropic"}}),
),
(
BridgeRejectionCause::LiveAdapterUnavailable {
provider: "gemini".to_string(),
},
json!({"live_adapter_unavailable": {"provider": "gemini"}}),
),
(
BridgeRejectionCause::LiveTransportUnsupported {
requested: "webrtc".to_string(),
},
json!({"live_transport_unsupported": {"requested": "webrtc"}}),
),
(
BridgeRejectionCause::CapabilityMissing {
capability: "durable_sessions".to_string(),
},
json!({"capability_missing": {"capability": "durable_sessions"}}),
),
];
for (cause, expected) in cases {
let value = serde_json::to_value(&cause).expect("serialize cause");
assert_eq!(value, expected, "cause {cause:?} wire shape");
let decoded: BridgeRejectionCause =
serde_json::from_value(value).expect("decode cause");
assert_eq!(decoded, cause);
}
}
#[test]
fn connection_target_error_kind_round_trips_snake_case() {
let cases: &[(ConnectionTargetErrorKind, &str)] = &[
(ConnectionTargetErrorKind::MissingRealm, "missing_realm"),
(ConnectionTargetErrorKind::UnknownRealm, "unknown_realm"),
(
ConnectionTargetErrorKind::MissingDefaultBinding,
"missing_default_binding",
),
(
ConnectionTargetErrorKind::InvalidRealmId,
"invalid_realm_id",
),
(
ConnectionTargetErrorKind::InvalidBindingId,
"invalid_binding_id",
),
(
ConnectionTargetErrorKind::RealmConfigInvalid,
"realm_config_invalid",
),
(ConnectionTargetErrorKind::BindingInvalid, "binding_invalid"),
(
ConnectionTargetErrorKind::ProviderMismatch,
"provider_mismatch",
),
(ConnectionTargetErrorKind::RealmChain, "realm_chain"),
];
for (kind, expected) in cases {
let value = serde_json::to_value(kind).expect("serialize kind");
assert_eq!(value, json!(expected));
let decoded: ConnectionTargetErrorKind =
serde_json::from_value(value).expect("decode kind");
assert_eq!(decoded, *kind);
}
}
#[test]
fn member_build_rejection_round_trips_every_variant() {
let cases: Vec<(MemberBuildRejection, serde_json::Value)> = vec![
(
MemberBuildRejection::UnknownProviderForModel {
model: "m1".to_string(),
},
json!({"unknown_provider_for_model": {"model": "m1"}}),
),
(
MemberBuildRejection::BindingUnresolvable {
kind: ConnectionTargetErrorKind::MissingDefaultBinding,
},
json!({"binding_unresolvable": {"kind": "missing_default_binding"}}),
),
(
MemberBuildRejection::ProviderAuth {
kind: meerkat_core::AuthErrorKind::InteractiveLoginRequired,
},
json!({"provider_auth": {"kind": "interactive_login_required"}}),
),
(
MemberBuildRejection::SelfHostedServerMissing {
server_id: "llama-box".to_string(),
},
json!({"self_hosted_server_missing": {"server_id": "llama-box"}}),
),
];
for (rejection, expected) in cases {
let value = serde_json::to_value(&rejection).expect("serialize rejection");
assert_eq!(value, expected, "rejection {rejection:?} wire shape");
let decoded: MemberBuildRejection =
serde_json::from_value(value).expect("decode rejection");
assert_eq!(decoded, rejection);
}
}
fn assert_reply_round_trip(reply: BridgeReply, expected: serde_json::Value) {
let value = serde_json::to_value(&reply).expect("serialize reply");
assert_eq!(value, expected, "reply wire shape must be stable");
let _decoded: BridgeReply = serde_json::from_value(value).expect("decode reply");
}
#[test]
fn bridge_reply_unknown_field_fails_closed() {
let err = serde_json::from_value::<BridgeReply>(json!({
"result": "ack",
"ok": true,
"extra_behavior": true,
}))
.expect_err("unknown reply fields must fail at serde boundary");
let message = err.to_string();
assert!(
message.contains("extra_behavior") || message.contains("unknown field"),
"expected unknown field error, got: {message}"
);
}
#[test]
fn bridge_reply_bind_member_ack_round_trip() {
assert_reply_round_trip(
BridgeReply::BindMember(BridgeBindResponse {
peer_id: "peer-x".to_string(),
address: "inproc://peer-x".to_string(),
capabilities: BridgeCapabilities::default(),
}),
json!({
"result": "bind_member",
"peer_id": "peer-x",
"address": "inproc://peer-x",
"capabilities": {
"current_protocol_version": SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
"default_protocol_version": SUPERVISOR_BRIDGE_DEFAULT_PROTOCOL_VERSION,
"supported_protocol_versions": [
BridgeProtocolVersion::V2,
BridgeProtocolVersion::V3,
BridgeProtocolVersion::V4,
],
"deliver_member_input": false,
"observe_member": false,
"interrupt_member": false,
"hard_cancel_member": false,
"retire_member": false,
"destroy_member": false,
"wire_member": false,
"unwire_member": false,
},
}),
);
}
#[test]
fn bridge_reply_ack_round_trip() {
assert_reply_round_trip(
BridgeReply::Ack(BridgeAck { ok: true }),
json!({ "result": "ack", "ok": true }),
);
}
#[test]
fn bridge_reply_observation_round_trip() {
assert_reply_round_trip(
BridgeReply::Observation(BridgeObservationResponse {
state: BridgeMemberRuntimeState::Running,
accepting_inputs: None,
current_run_id: None,
peer_connectivity: None,
last_error: None,
observed_at: "2026-04-17T00:00:00Z".to_string(),
}),
json!({
"result": "observation",
"state": "running",
"observed_at": "2026-04-17T00:00:00Z",
}),
);
}
#[test]
fn bridge_reply_delivery_round_trip() {
assert_reply_round_trip(
BridgeReply::Delivery(BridgeDeliveryResponse {
input_id: "in-1".to_string(),
canonical_input_id: None,
outcome: BridgeDeliveryOutcome::Accepted,
}),
json!({
"result": "delivery",
"input_id": "in-1",
"canonical_input_id": null,
"outcome": { "outcome": "accepted" },
}),
);
assert_reply_round_trip(
BridgeReply::Delivery(BridgeDeliveryResponse {
input_id: "in-2".to_string(),
canonical_input_id: None,
outcome: BridgeDeliveryOutcome::Rejected {
cause: BridgeDeliveryRejectionCause::DurabilityViolation {
detail: "derived durable input cannot be accepted".to_string(),
},
reason: "derived durable input cannot be accepted".to_string(),
},
}),
json!({
"result": "delivery",
"input_id": "in-2",
"canonical_input_id": null,
"outcome": {
"outcome": "rejected",
"cause": {
"kind": "durability_violation",
"detail": "derived durable input cannot be accepted",
},
"reason": "derived durable input cannot be accepted",
},
}),
);
assert_reply_round_trip(
BridgeReply::Delivery(BridgeDeliveryResponse {
input_id: "in-3".to_string(),
canonical_input_id: None,
outcome: BridgeDeliveryOutcome::Rejected {
cause: BridgeDeliveryRejectionCause::OutcomeJournalFull {
retained: 256,
limit: 256,
},
reason: "consume and acknowledge outcomes".to_string(),
},
}),
json!({
"result": "delivery",
"input_id": "in-3",
"canonical_input_id": null,
"outcome": {
"outcome": "rejected",
"cause": {
"kind": "outcome_journal_full",
"retained": 256,
"limit": 256,
},
"reason": "consume and acknowledge outcomes",
},
}),
);
}
#[test]
fn bridge_delivery_not_ready_carries_typed_member_state() {
let outcome = BridgeDeliveryOutcome::Rejected {
cause: BridgeDeliveryRejectionCause::NotReady {
state: BridgeMemberRuntimeState::Stopped,
},
reason: "runtime not accepting input while in state: stopped".to_string(),
};
let value = serde_json::to_value(&outcome).expect("serialize outcome");
assert_eq!(
value,
json!({
"outcome": "rejected",
"cause": {
"kind": "not_ready",
"state": "stopped",
},
"reason": "runtime not accepting input while in state: stopped",
})
);
let decoded: BridgeDeliveryOutcome =
serde_json::from_value(value).expect("legacy wire state string remains compatible");
assert_eq!(decoded, outcome);
}
#[test]
fn bridge_reply_retire_round_trip() {
assert_reply_round_trip(
BridgeReply::Retire(BridgeRetireResponse {
inputs_abandoned: 2,
inputs_pending_drain: 0,
}),
json!({
"result": "retire",
"inputs_abandoned": 2,
"inputs_pending_drain": 0,
}),
);
}
#[test]
fn bridge_reply_destroy_round_trip() {
assert_reply_round_trip(
BridgeReply::Destroy(BridgeDestroyResponse {
inputs_abandoned: 3,
}),
json!({
"result": "destroy",
"inputs_abandoned": 3,
}),
);
}
#[test]
fn bridge_bootstrap_token_debug_redacts_nonempty_body() {
let token = BridgeBootstrapToken::new("super-secret-bootstrap");
let rendered = format!("{token:?}");
assert_eq!(
rendered,
format!(
"BridgeBootstrapToken(<redacted, {}B>)",
"super-secret-bootstrap".len()
)
);
assert!(
!rendered.contains("super-secret-bootstrap"),
"Debug output must not contain the raw token body"
);
}
#[test]
fn bridge_bootstrap_token_debug_marks_empty_token() {
let token = BridgeBootstrapToken::new("");
assert_eq!(format!("{token:?}"), "BridgeBootstrapToken(empty)");
}
#[test]
fn bridge_bootstrap_token_serde_is_transparent_over_string() {
let token = BridgeBootstrapToken::new("tok-abc");
let value = serde_json::to_value(&token).expect("serialize token");
assert_eq!(value, json!("tok-abc"));
let decoded: BridgeBootstrapToken =
serde_json::from_value(json!("tok-abc")).expect("decode token");
assert_eq!(decoded, token);
let s = serde_json::to_string(&token).expect("serialize string");
assert_eq!(s, "\"tok-abc\"");
}
#[test]
fn bridge_bind_payload_wire_compat_with_plain_string_bootstrap_token() {
let raw = json!({
"supervisor": {
"name": "mob/__mob_supervisor__",
"peer_id": "00000000-0000-0000-0000-00000000bbbb",
"address": "inproc://mob/__mob_supervisor__",
},
"epoch": 7,
"protocol_version": SUPERVISOR_BRIDGE_PROTOCOL_VERSION,
"expected_peer_id": "00000000-0000-0000-0000-00000000aaaa",
"expected_address": "inproc://member",
"bootstrap_token": "tok-raw-string",
});
let payload: BridgeBindPayload =
serde_json::from_value(raw.clone()).expect("decode pre-newtype payload");
assert_eq!(payload.bootstrap_token.as_str(), "tok-raw-string");
assert_eq!(payload.supervisor.pubkey, [0u8; 32]);
let reencoded = serde_json::to_value(&payload).expect("reserialize payload");
let mut expected = raw;
expected["supervisor"]["pubkey"] = json!(vec![0u8; 32]);
assert_eq!(
reencoded, expected,
"pre-pubkey payloads must decode and reserialize with the defaulted pubkey"
);
}
fn v4() -> BridgeProtocolVersion {
BridgeProtocolVersion::V4
}
fn sample_spawn_spec() -> MemberOperatorSpawnSpec {
MemberOperatorSpawnSpec {
profile: "worker".to_string(),
member_id: "worker-2".to_string(),
initial_message: Some(meerkat_core::types::ContentInput::Text("go".to_string())),
runtime_mode: Some(WireMobRuntimeMode::TurnDriven),
launch_mode: Some(WireMemberLaunchMode::Fresh),
auto_wire_parent: Some(true),
placement: Some("host-b-peer-id".to_string()),
requested_tool_access_policy_present: true,
resolved_tool_access_policy: Some(WireResolvedToolAccessPolicy::AllowList(vec![
"member_status".to_string(),
])),
}
}
fn all_v4_commands() -> Vec<BridgeCommand> {
vec![
BridgeCommand::HardCancelMember(sample_hard_cancel_payload()),
BridgeCommand::CancelTrackedMemberInput(sample_tracked_input_cancel_payload()),
BridgeCommand::ReadMemberHistory(BridgeReadHistoryPayload {
supervisor: sample_peer_spec(),
epoch: 9,
protocol_version: v4(),
expected_member: sample_member_incarnation(),
from_index: Some(10),
limit: Some(50),
}),
BridgeCommand::PollMemberEvents(BridgePollEventsPayload {
supervisor: sample_peer_spec(),
epoch: 9,
protocol_version: v4(),
expected_member: sample_member_incarnation(),
cursor: BridgeEventCursor::At {
generation: 2,
seq: 77,
},
max: Some(100),
outcome_acks: vec![BridgeTurnOutcomeAck {
generation: 2,
fence_token: 9,
input_id: "input-previous".to_string(),
}],
max_outcomes: Some(8),
wait_ms: Some(2_000),
}),
BridgeCommand::OpenMemberLiveChannel(BridgeLiveOpenPayload {
supervisor: sample_peer_spec(),
epoch: 9,
protocol_version: v4(),
expected_member: sample_member_incarnation(),
turning_mode: Some(RealtimeTurningMode::ExplicitCommit),
transport: Some(LiveOpenTransport::Websocket),
}),
BridgeCommand::CloseMemberLiveChannel(BridgeLiveChannelPayload {
supervisor: sample_peer_spec(),
epoch: 9,
protocol_version: v4(),
expected_member: sample_member_incarnation(),
channel_id: "chan-1".to_string(),
}),
BridgeCommand::MemberLiveChannelStatus(BridgeLiveStatusPayload {
supervisor: sample_peer_spec(),
epoch: 9,
protocol_version: v4(),
expected_member: sample_member_incarnation(),
channel_id: Some("chan-1".to_string()),
}),
BridgeCommand::MemberLiveChannelStatus(BridgeLiveStatusPayload {
supervisor: sample_peer_spec(),
epoch: 9,
protocol_version: v4(),
expected_member: sample_member_incarnation(),
channel_id: None,
}),
BridgeCommand::ControlMemberLiveChannel(BridgeLiveControlPayload {
supervisor: sample_peer_spec(),
epoch: 9,
protocol_version: v4(),
expected_member: sample_member_incarnation(),
channel_id: "chan-1".to_string(),
verb: BridgeLiveControlVerb::Truncate {
item_id: "item-3".to_string(),
content_index: 0,
audio_played_ms: 1_500,
},
}),
BridgeCommand::BindHost(BridgeHostBindPayload {
supervisor: sample_peer_spec(),
epoch: 1,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
expected_host_peer_id: "host-peer".to_string(),
expected_address: "tcp://10.0.0.2:7100".to_string(),
bootstrap_proof: BridgeHostBootstrapProof::new("host-bootstrap-proof"),
required_capabilities: Default::default(),
}),
BridgeCommand::RebindHost(BridgeHostRebindPayload {
supervisor: sample_peer_spec(),
epoch: 2,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
required_capabilities: Default::default(),
}),
BridgeCommand::RevokeHost(BridgeHostRevokePayload {
supervisor: sample_peer_spec(),
epoch: 2,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
}),
BridgeCommand::MaterializeMember(Box::new(BridgeMaterializePayload {
supervisor: sample_peer_spec(),
epoch: 1,
binding_generation: 1,
protocol_version: v4(),
generation: 1,
fence_token: 3,
spec: crate::wire::portable_spec::sample_portable_member_spec(),
spec_digest: "d".repeat(64),
launch: MaterializeLaunchMode::Resume {
session_id: "sess-9".to_string(),
},
})),
BridgeCommand::ReleaseMember(BridgeReleasePayload {
supervisor: sample_peer_spec(),
epoch: 1,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
agent_identity: "worker-1".to_string(),
generation: 1,
fence_token: 3,
}),
BridgeCommand::InstallPeerTrust(BridgePeerTrustPayload {
supervisor: sample_peer_spec(),
epoch: 1,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
agent_identity: "worker-1".to_string(),
peer: sample_peer_spec(),
}),
BridgeCommand::RemovePeerTrust(BridgePeerTrustPayload {
supervisor: sample_peer_spec(),
epoch: 1,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
agent_identity: "worker-1".to_string(),
peer: sample_peer_spec(),
}),
BridgeCommand::HostStatus(BridgeHostStatusPayload {
supervisor: sample_peer_spec(),
epoch: 1,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
}),
BridgeCommand::MemberOperatorRequest(BridgeMemberOperatorPayload {
agent_identity: "worker-1".to_string(),
requester_generation: 2,
requester_fence_token: 9,
requester_host_id: "host-a".to_string(),
requester_host_binding_generation: 4,
requester_member_session_id: "member-session-a".to_string(),
request_id: "req-1".to_string(),
op: MemberOperatorOp::SpawnMember(Box::new(sample_spawn_spec())),
protocol_version: v4(),
}),
]
}
#[test]
fn v4_commands_round_trip_and_decode_through_versioned_decoder() {
for cmd in all_v4_commands() {
assert_command_round_trip(&cmd);
assert_eq!(cmd.protocol_version(), BridgeProtocolVersion::V4);
let value = serde_json::to_value(&cmd).expect("serialize command");
decode_bridge_command(value).expect("V4 command must decode on a V4 binary");
}
}
#[test]
fn v4_command_with_future_protocol_version_rejects_before_serde() {
for cmd in all_v4_commands() {
let mut value = serde_json::to_value(&cmd).expect("serialize command");
value
.as_object_mut()
.expect("command object")
.insert("protocol_version".to_string(), json!(5));
let err = decode_bridge_command(value)
.expect_err("future protocol version must reject pre-serde");
assert!(
matches!(err, BridgeCommandDecodeError::UnsupportedProtocolVersion(_)),
"expected typed UnsupportedProtocolVersion, got {err:?}"
);
}
}
#[test]
fn every_v4_only_command_rejects_v3_with_typed_version_error() {
let mut commands = all_v4_commands();
commands.push(BridgeCommand::DeliverMemberInput(BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: BridgeProtocolVersion::V4,
input_id: "placed-v4".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("placed".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: Some(BridgeMemberIncarnation {
mob_id: "mob-1".to_string(),
agent_identity: "worker-1".to_string(),
host_id: "host-1".to_string(),
binding_generation: 1,
member_session_id: "session-1".to_string(),
generation: 3,
fence_token: 7,
}),
injected_context: Vec::new(),
turn: None,
outcome_tracking: None,
}));
commands.push(BridgeCommand::DeliverMemberInput(BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: BridgeProtocolVersion::V4,
input_id: "turn-v4".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("turn".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: None,
injected_context: Vec::new(),
turn: Some(BridgeTurnDirective {
correlation: BridgeTurnCorrelation {
run_id: "run-1".to_string(),
step_id: "step-1".to_string(),
},
tool_overlay: None,
}),
outcome_tracking: None,
}));
for command in commands {
let mut value = serde_json::to_value(&command).expect("serialize V4 command");
value["protocol_version"] = json!(3);
let error = decode_bridge_command(value)
.expect_err("a V4-only command stamped V3 must reject before serving");
assert!(
matches!(
error,
BridgeCommandDecodeError::UnsupportedProtocolVersion(_)
),
"expected typed version rejection for {command:?}, got {error:?}"
);
assert!(error.to_string().contains("minimum 4"), "{error}");
}
}
#[test]
fn old_v3_hard_cancel_shape_rejects_by_version_before_new_fields() {
let fixture = json!({
"command": "hard_cancel_member",
"supervisor": sample_peer_spec(),
"epoch": 4,
"protocol_version": 3,
"reason": "legacy immediate cancel"
});
let error = decode_bridge_command(fixture)
.expect_err("old V3 hard cancel must not decode as the V4 exact-run shape");
assert!(
matches!(
error,
BridgeCommandDecodeError::UnsupportedProtocolVersion(_)
),
"expected typed version rejection, got {error:?}"
);
assert!(error.to_string().contains("HardCancelMember"));
}
#[test]
fn v3_capabilities_fixture_decodes_and_default_extensions_omit() {
let fixture = json!({
"current_protocol_version": 3,
"default_protocol_version": 3,
"supported_protocol_versions": [2, 3],
"deliver_member_input": true,
"observe_member": true,
"interrupt_member": true,
"hard_cancel_member": false,
"retire_member": true,
"destroy_member": true,
"wire_member": true,
"unwire_member": true
});
let capabilities: BridgeCapabilities =
serde_json::from_value(fixture).expect("origin-V3 capabilities must decode");
assert!(!capabilities.durable_sessions);
assert!(capabilities.engine_version.is_empty());
let encoded = serde_json::to_value(BridgeCapabilities {
current_protocol_version: BridgeProtocolVersion::V3,
default_protocol_version: BridgeProtocolVersion::V3,
supported_protocol_versions: vec![BridgeProtocolVersion::V2, BridgeProtocolVersion::V3],
..BridgeCapabilities::default()
})
.expect("serialize V3 projection");
for field in [
"durable_sessions",
"autonomous_members",
"tracked_input_cancel",
"memory_store",
"mcp",
"engine_version",
"approval_forwarding",
"resolvable_providers",
] {
assert!(encoded.get(field).is_none(), "V4 field leaked: {field}");
}
}
#[test]
fn host_addressed_payloads_require_explicit_mob_id() {
let host_addressed = [
"bind_host",
"rebind_host",
"revoke_host",
"release_member",
"install_peer_trust",
"remove_peer_trust",
"host_status",
];
let mut seen = Vec::new();
for cmd in all_v4_commands() {
let mut value = serde_json::to_value(&cmd).expect("serialize command");
let tag = value["command"]
.as_str()
.expect("command tag present")
.to_string();
if !host_addressed.contains(&tag.as_str()) {
continue;
}
seen.push(tag.clone());
assert_eq!(
value["mob_id"],
json!("mob-1"),
"{tag} must carry the explicit mob_id"
);
value
.as_object_mut()
.expect("command object")
.remove("mob_id");
assert!(
decode_bridge_command(value).is_err(),
"{tag} without mob_id must fail decode (never name-derived identity)"
);
}
for tag in host_addressed {
assert!(
seen.contains(&tag.to_string()),
"fixture inventory must cover {tag}"
);
}
}
#[test]
fn materialize_payload_rejects_deleted_budget_seed() {
let cmd = BridgeCommand::MaterializeMember(Box::new(BridgeMaterializePayload {
supervisor: sample_peer_spec(),
epoch: 1,
binding_generation: 1,
protocol_version: v4(),
generation: 1,
fence_token: 3,
spec: crate::wire::portable_spec::sample_portable_member_spec(),
spec_digest: "d".repeat(64),
launch: MaterializeLaunchMode::Fresh {},
}));
let mut value = serde_json::to_value(&cmd).expect("serialize command");
value
.as_object_mut()
.expect("command object")
.insert("budget_seed".to_string(), json!({ "max_tokens": 10_000 }));
let err = decode_bridge_command(value)
.expect_err("deleted budget_seed must fail closed at the wire boundary");
assert!(
err.to_string().contains("unknown field `budget_seed`"),
"unexpected error: {err}"
);
}
#[test]
fn materialize_payload_rejects_duplicate_agent_identity_carrier() {
let cmd = BridgeCommand::MaterializeMember(Box::new(BridgeMaterializePayload {
supervisor: sample_peer_spec(),
epoch: 1,
binding_generation: 1,
protocol_version: v4(),
generation: 1,
fence_token: 3,
spec: crate::wire::portable_spec::sample_portable_member_spec(),
spec_digest: "d".repeat(64),
launch: MaterializeLaunchMode::Fresh {},
}));
let mut value = serde_json::to_value(&cmd).expect("serialize command");
value
.as_object_mut()
.expect("command object")
.insert("agent_identity".to_string(), json!("different-worker"));
let err = decode_bridge_command(value)
.expect_err("duplicate agent_identity carrier must fail closed");
assert!(
err.to_string().contains("unknown field `agent_identity`"),
"unexpected error: {err}"
);
}
#[test]
fn rebind_host_round_trips_and_fails_closed() {
let cmd = BridgeCommand::RebindHost(BridgeHostRebindPayload {
supervisor: sample_peer_spec(),
epoch: 5,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
required_capabilities: Default::default(),
});
assert_command_round_trip(&cmd);
let mut value = serde_json::to_value(&cmd).expect("serialize command");
assert_eq!(value["command"], json!("rebind_host"));
value
.as_object_mut()
.expect("command object")
.insert("smuggled".to_string(), json!(true));
assert!(
decode_bridge_command(value).is_err(),
"unknown RebindHost fields must fail closed"
);
let reply = BridgeReply::HostRebound(BridgeHostReboundResponse {
host_peer_id: "host-peer".to_string(),
binding_generation: 1,
capabilities: BridgeCapabilities::default(),
live_endpoint: None,
});
let value = serde_json::to_value(&reply).expect("serialize reply");
assert_eq!(value["result"], json!("host_rebound"));
assert!(
value.get("live_endpoint").is_none(),
"absent live endpoint must be omitted (absence = live-incapable, no shadow)"
);
assert_reply_value_round_trip(&reply);
}
#[test]
fn revoke_host_round_trips_and_requires_exact_closed_fields() {
let cmd = BridgeCommand::RevokeHost(BridgeHostRevokePayload {
supervisor: sample_peer_spec(),
epoch: 5,
binding_generation: 1,
protocol_version: v4(),
mob_id: "mob-1".to_string(),
});
assert_command_round_trip(&cmd);
let mut value = serde_json::to_value(&cmd).expect("serialize command");
assert_eq!(value["command"], json!("revoke_host"));
value
.as_object_mut()
.expect("command object")
.insert("smuggled".to_string(), json!(true));
assert!(
decode_bridge_command(value).is_err(),
"unknown RevokeHost fields must fail closed"
);
let reply = BridgeReply::HostRevoked(BridgeHostRevokedResponse {
host_peer_id: "host-peer".to_string(),
mob_id: "mob-1".to_string(),
epoch: 5,
binding_generation: 1,
released_members: vec!["worker-1".to_string()],
});
let value = serde_json::to_value(&reply).expect("serialize reply");
assert_eq!(value["result"], json!("host_revoked"));
assert_reply_value_round_trip(&reply);
}
#[test]
fn v3_delivery_fixture_without_turn_still_decodes() {
let fixture = json!({
"command": "deliver_member_input",
"supervisor": {
"name": "mob/__mob_supervisor__",
"peer_id": "00000000-0000-0000-0000-00000000bbbb",
"address": "inproc://mob/__mob_supervisor__",
},
"epoch": 7,
"protocol_version": 3,
"input_id": "input-legacy",
"content": "hello from a V3 sender",
"handling_mode": "queue",
});
match decode_bridge_command(fixture).expect("V3 delivery fixture decodes") {
BridgeCommand::DeliverMemberInput(payload) => {
assert_eq!(payload.protocol_version, BridgeProtocolVersion::V3);
assert!(payload.turn.is_none(), "absent turn decodes as None");
assert!(
payload.outcome_tracking.is_none(),
"absent tracking marker decodes as None"
);
assert!(payload.injected_context.is_empty());
}
other => panic!("expected DeliverMemberInput, got {other:?}"),
}
}
#[test]
fn delivery_with_turn_directive_round_trips_and_serializes_turn_key() {
let payload = BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: v4(),
input_id: "input-directed".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("step".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: None,
injected_context: Vec::new(),
turn: Some(BridgeTurnDirective {
correlation: BridgeTurnCorrelation {
run_id: "run-1".to_string(),
step_id: "draft".to_string(),
},
tool_overlay: Some(meerkat_core::service::PublicTurnToolOverlay {
allowed_tools: None,
blocked_tools: None,
}),
}),
outcome_tracking: None,
};
let value = serde_json::to_value(&payload).expect("serialize payload");
assert_eq!(value["turn"]["correlation"]["run_id"], json!("run-1"));
let decoded: BridgeDeliveryPayload = serde_json::from_value(value).expect("decode payload");
assert_eq!(decoded, payload);
assert_command_round_trip(&BridgeCommand::DeliverMemberInput(payload));
}
#[test]
fn turn_bearing_delivery_fails_closed_on_pre_v4_receiver_shape() {
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[allow(dead_code)]
struct PreV4DeliveryPayload {
supervisor: BridgePeerSpec,
epoch: u64,
protocol_version: BridgeProtocolVersion,
input_id: String,
content: meerkat_core::types::ContentInput,
handling_mode: meerkat_core::types::HandlingMode,
#[serde(default)]
injected_context: Vec<meerkat_core::types::ContentInput>,
}
let mut value = serde_json::to_value(BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: BridgeProtocolVersion::V3,
input_id: "input-directed".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("step".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: None,
injected_context: Vec::new(),
turn: None,
outcome_tracking: None,
})
.expect("serialize payload");
value["turn"] = json!({
"correlation": { "run_id": "run-1", "step_id": "draft" },
});
let err = serde_json::from_value::<PreV4DeliveryPayload>(value)
.expect_err("old receivers must reject a turn-bearing delivery");
assert!(
err.to_string().contains("turn") || err.to_string().contains("unknown field"),
"unexpected error: {err}"
);
}
#[test]
fn tracked_interaction_fails_closed_on_pre_marker_v4_receiver_shape() {
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[allow(dead_code)]
struct PreTrackingDeliveryPayload {
supervisor: BridgePeerSpec,
epoch: u64,
protocol_version: BridgeProtocolVersion,
input_id: String,
content: meerkat_core::types::ContentInput,
handling_mode: meerkat_core::types::HandlingMode,
#[serde(default)]
objective_id: Option<meerkat_core::interaction::ObjectiveId>,
#[serde(default)]
expected_member: Option<BridgeMemberIncarnation>,
#[serde(default)]
injected_context: Vec<meerkat_core::types::ContentInput>,
#[serde(default)]
turn: Option<BridgeTurnDirective>,
}
let value = serde_json::to_value(BridgeDeliveryPayload {
objective_id: None,
supervisor: sample_peer_spec(),
epoch: 2,
protocol_version: BridgeProtocolVersion::V4,
input_id: "input-tracked".to_string(),
transcript_interaction_id: None,
content: meerkat_core::types::ContentInput::Text("work".to_string()),
handling_mode: meerkat_core::types::HandlingMode::Queue,
expected_member: Some(sample_member_incarnation()),
injected_context: Vec::new(),
turn: None,
outcome_tracking: Some(BridgeOutcomeTracking::Interaction),
})
.expect("serialize tracked interaction");
let error = serde_json::from_value::<PreTrackingDeliveryPayload>(value)
.expect_err("pre-marker V4 receivers must reject explicit outcome custody");
assert!(
error.to_string().contains("outcome_tracking")
|| error.to_string().contains("unknown field"),
"unexpected error: {error}"
);
}
#[test]
fn materialize_launch_mode_is_closed_fresh_resume_only() {
let fresh: MaterializeLaunchMode =
serde_json::from_value(json!({"mode": "fresh"})).expect("fresh decodes");
assert_eq!(fresh, MaterializeLaunchMode::Fresh {});
let resume: MaterializeLaunchMode =
serde_json::from_value(json!({"mode": "resume", "session_id": "sess-1"}))
.expect("resume decodes");
assert_eq!(
resume,
MaterializeLaunchMode::Resume {
session_id: "sess-1".to_string()
}
);
assert!(
serde_json::from_value::<MaterializeLaunchMode>(json!({"mode": "fork"})).is_err(),
"fork must be unrepresentable on the materialize wire (A6/§19.L1)"
);
assert!(
serde_json::from_value::<MaterializeLaunchMode>(json!({
"mode": "fresh",
"source_member_id": "smuggled",
}))
.is_err(),
"unknown launch-mode fields must fail closed"
);
}
#[test]
fn materialize_launch_outcome_and_disposal_round_trip_snake_case() {
for (outcome, expected) in [
(MaterializeLaunchOutcome::Fresh, json!("fresh")),
(MaterializeLaunchOutcome::ResumedLive, json!("resumed_live")),
(
MaterializeLaunchOutcome::ResumedFromSnapshot,
json!("resumed_from_snapshot"),
),
] {
let value = serde_json::to_value(outcome).expect("serialize outcome");
assert_eq!(value, expected);
let decoded: MaterializeLaunchOutcome =
serde_json::from_value(value).expect("decode outcome");
assert_eq!(decoded, outcome);
}
for (disposal, expected) in [
(
MemberSessionDisposal::Archived,
json!({"disposal": "archived"}),
),
(
MemberSessionDisposal::AlreadyArchived,
json!({"disposal": "already_archived"}),
),
(
MemberSessionDisposal::RuntimeReleasedOnly {
cause: RuntimeReleaseCause::NoDurableSessions,
},
json!({"disposal": "runtime_released_only", "cause": "no_durable_sessions"}),
),
(
MemberSessionDisposal::RuntimeReleasedOnly {
cause: RuntimeReleaseCause::HostOwnedSession,
},
json!({"disposal": "runtime_released_only", "cause": "host_owned_session"}),
),
] {
let value = serde_json::to_value(disposal).expect("serialize disposal");
assert_eq!(value, expected);
let decoded: MemberSessionDisposal =
serde_json::from_value(value).expect("decode disposal");
assert_eq!(decoded, disposal);
}
}
#[test]
fn bridge_event_cursor_round_trips_and_rejects_unknown_fields() {
let tail = serde_json::to_value(BridgeEventCursor::Tail).expect("serialize tail");
assert_eq!(tail, json!({"cursor": "tail"}));
let at = serde_json::to_value(BridgeEventCursor::At {
generation: 2,
seq: 77,
})
.expect("serialize at");
assert_eq!(at, json!({"cursor": "at", "generation": 2, "seq": 77}));
let decoded: BridgeEventCursor = serde_json::from_value(at).expect("decode at");
assert_eq!(
decoded,
BridgeEventCursor::At {
generation: 2,
seq: 77
}
);
assert!(
serde_json::from_value::<BridgeEventCursor>(
json!({"cursor": "at", "generation": 2, "seq": 7, "stream": "task"})
)
.is_err(),
"unknown cursor fields must fail closed (seq domain is durable StoredEvent.seq only)"
);
}
#[test]
fn wire_flow_turn_outcome_round_trips_all_terminals() {
let cases: Vec<(WireFlowTurnOutcome, serde_json::Value)> = vec![
(WireFlowTurnOutcome::RunCompleted, json!("run_completed")),
(
WireFlowTurnOutcome::ExtractionSucceeded,
json!("extraction_succeeded"),
),
(
WireFlowTurnOutcome::ExtractionFailed {
detail: WireFlowFailureDetail::complete("schema".to_string()),
},
json!({"extraction_failed": {"detail": {
"text": "schema",
"original_utf8_bytes": 6,
"truncated": false
}}}),
),
(
WireFlowTurnOutcome::RunFailed {
detail: WireFlowFailureDetail::complete("boom".to_string()),
},
json!({"run_failed": {"detail": {
"text": "boom",
"original_utf8_bytes": 4,
"truncated": false
}}}),
),
(
WireFlowTurnOutcome::InteractionComplete,
json!("interaction_complete"),
),
(
WireFlowTurnOutcome::InteractionCallbackPending,
json!("interaction_callback_pending"),
),
(
WireFlowTurnOutcome::InteractionFailed {
detail: WireFlowFailureDetail::complete("timeout".to_string()),
},
json!({"interaction_failed": {"detail": {
"text": "timeout",
"original_utf8_bytes": 7,
"truncated": false
}}}),
),
(WireFlowTurnOutcome::ChannelClosed, json!("channel_closed")),
];
assert_eq!(
cases.len(),
8,
"seven flow-turn terminals + channel-close (§18.1/§18.11)"
);
for (outcome, expected) in cases {
let value = serde_json::to_value(&outcome).expect("serialize outcome");
assert_eq!(value, expected, "outcome {outcome:?} wire shape");
let decoded: WireFlowTurnOutcome =
serde_json::from_value(value).expect("decode outcome");
assert_eq!(decoded, outcome);
}
let cursor = MemberEventCursor {
generation: 3,
seq: 41,
};
let value = serde_json::to_value(cursor).expect("serialize member event cursor");
assert_eq!(value, json!({"generation": 3, "seq": 41}));
let decoded: MemberEventCursor = serde_json::from_value(value).expect("decode cursor");
assert_eq!(decoded, cursor);
}
#[test]
fn member_operator_op_covers_exactly_the_twelve_operator_tools() {
let ops: Vec<(MemberOperatorOp, &str)> = vec![
(
MemberOperatorOp::SpawnMember(Box::new(sample_spawn_spec())),
"spawn_member",
),
(
MemberOperatorOp::SpawnManyMembers {
specs: vec![sample_spawn_spec()],
},
"spawn_many_members",
),
(
MemberOperatorOp::RetireMember {
member_id: "w1".to_string(),
},
"retire_member",
),
(
MemberOperatorOp::ForceCancelMember {
member_id: "w1".to_string(),
},
"force_cancel_member",
),
(
MemberOperatorOp::MemberStatus {
member_id: "w1".to_string(),
},
"member_status",
),
(
MemberOperatorOp::WireMembers {
member_id: "w1".to_string(),
peer_member_id: "w2".to_string(),
},
"wire_members",
),
(
MemberOperatorOp::UnwireMembers {
member_id: "w1".to_string(),
peer_member_id: "w2".to_string(),
},
"unwire_members",
),
(MemberOperatorOp::ListMembers, "list_members"),
(MemberOperatorOp::MobListFlows, "mob_list_flows"),
(
MemberOperatorOp::MobRunFlow {
flow_id: "review".to_string(),
params: Some(WireOpaqueJson::from_value(&json!({"prompt": "go"}))),
},
"mob_run_flow",
),
(
MemberOperatorOp::MobFlowStatus {
run_id: "run-1".to_string(),
},
"mob_flow_status",
),
(
MemberOperatorOp::MobCancelFlow {
run_id: "run-1".to_string(),
},
"mob_cancel_flow",
),
];
assert_eq!(ops.len(), 12, "exactly twelve operator ops (§15.10)");
for (op, tag) in &ops {
match op {
MemberOperatorOp::SpawnMember(_)
| MemberOperatorOp::SpawnManyMembers { .. }
| MemberOperatorOp::RetireMember { .. }
| MemberOperatorOp::ForceCancelMember { .. }
| MemberOperatorOp::MemberStatus { .. }
| MemberOperatorOp::WireMembers { .. }
| MemberOperatorOp::UnwireMembers { .. }
| MemberOperatorOp::ListMembers
| MemberOperatorOp::MobListFlows
| MemberOperatorOp::MobRunFlow { .. }
| MemberOperatorOp::MobFlowStatus { .. }
| MemberOperatorOp::MobCancelFlow { .. } => {}
}
let value = serde_json::to_value(op).expect("serialize op");
assert_eq!(value["op"], json!(tag), "op tag pin for {tag}");
let decoded: MemberOperatorOp = serde_json::from_value(value).expect("decode op");
let reencoded = serde_json::to_value(&decoded).expect("reserialize op");
assert_eq!(
reencoded,
serde_json::to_value(op).expect("serialize op"),
"operator op round-trip must preserve wire shape"
);
}
assert!(
serde_json::from_value::<MemberOperatorOp>(json!({"op": "adopt_session"})).is_err(),
"unknown operator ops must fail decode (closed vocabulary)"
);
}
#[test]
fn spawn_op_carries_two_separate_tool_policy_facts() {
let value = serde_json::to_value(sample_spawn_spec()).expect("serialize spawn spec");
assert_eq!(value["requested_tool_access_policy_present"], json!(true));
assert_eq!(
value["resolved_tool_access_policy"]["type"],
json!("allow_list")
);
let mut without_requested = value.clone();
without_requested
.as_object_mut()
.expect("spawn spec object")
.remove("requested_tool_access_policy_present");
assert!(
serde_json::from_value::<MemberOperatorSpawnSpec>(without_requested).is_err(),
"requested_tool_access_policy_present is a required fact (O3)"
);
let mut with_inherit = value;
with_inherit["resolved_tool_access_policy"] = json!({"type": "inherit"});
assert!(
serde_json::from_value::<MemberOperatorSpawnSpec>(with_inherit).is_err(),
"inherit is unrepresentable in the resolved policy (O3)"
);
}
fn assert_reply_value_round_trip(reply: &BridgeReply) {
let value = serde_json::to_value(reply).expect("serialize reply");
let decoded: BridgeReply = serde_json::from_value(value.clone()).expect("decode reply");
let reencoded = serde_json::to_value(&decoded).expect("reserialize reply");
assert_eq!(
value, reencoded,
"reply round-trip must preserve wire shape"
);
}
#[test]
fn stale_member_residency_omits_absent_current_without_fabrication() {
let expected = sample_member_incarnation();
let cause = BridgeDeliveryRejectionCause::StaleMemberResidency {
expected: expected.clone(),
current: None,
};
let value = serde_json::to_value(&cause).expect("serialize stale residency cause");
assert_eq!(value["kind"], json!("stale_member_residency"));
assert_eq!(value["expected"]["host_id"], json!(expected.host_id));
assert_eq!(
value["expected"]["binding_generation"],
json!(expected.binding_generation)
);
assert!(
value.get("current").is_none(),
"an absent replacement is omitted instead of fabricated or encoded as an SDK-required null"
);
let decoded: BridgeDeliveryRejectionCause =
serde_json::from_value(value).expect("decode stale residency cause");
assert_eq!(decoded, cause);
assert!(cause.to_string().contains("current=None"));
}
#[test]
fn v4_replies_round_trip() {
let capabilities = BridgeCapabilities {
durable_sessions: true,
autonomous_members: true,
tracked_input_cancel: true,
engine_version: "0.7.22".to_string(),
resolvable_providers: vec![meerkat_core::Provider::Anthropic],
..BridgeCapabilities::default()
};
let replies = vec![
BridgeReply::BindHost(BridgeHostBindResponse {
host_peer_id: "host-peer".to_string(),
binding_generation: 1,
address: "tcp://10.0.0.2:7100".to_string(),
capabilities: capabilities.clone(),
live_endpoint: Some("wss://host-b:7443/live".to_string()),
}),
BridgeReply::HostRebound(BridgeHostReboundResponse {
host_peer_id: "host-peer".to_string(),
binding_generation: 1,
capabilities: capabilities.clone(),
live_endpoint: None,
}),
BridgeReply::HostRevoked(BridgeHostRevokedResponse {
host_peer_id: "host-peer".to_string(),
mob_id: "mob-1".to_string(),
epoch: 2,
binding_generation: 1,
released_members: vec!["worker-1".to_string()],
}),
BridgeReply::MemberHistoryPage(BridgeMemberHistoryPage {
generation: 2,
page: WireMemberHistoryPageBody {
from_index: 0,
messages: Vec::new(),
message_count: 0,
next_index: None,
complete: true,
},
}),
BridgeReply::MemberEventsPage(BridgeMemberEventsPage {
runtime_incarnation: BridgeHostRuntimeIncarnation::new(),
generation: 2,
fence_token: 9,
events: vec![WireEventRow {
durable_seq: 77,
envelope: meerkat_core::EventEnvelope::new(
"worker-1",
7,
Some("mob-1".to_string()),
meerkat_core::AgentEvent::TurnStarted { turn_number: 1 },
),
}],
from_seq: 77,
next_seq: 78,
watermark: 90,
turn_outcomes: vec![BridgeTurnOutcomeRecord {
input_id: "input-directed".to_string(),
generation: 2,
fence_token: 9,
terminal_seq: 77,
outcome: WireFlowTurnOutcome::ExtractionFailed {
detail: WireFlowFailureDetail::complete("schema mismatch".to_string()),
},
}],
outcomes_complete: true,
}),
BridgeReply::MemberMaterialized(BridgeMaterializedResponse {
member_pubkey: "ed25519:BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwc=".to_string(),
member_peer_id: "member-peer".to_string(),
advertised_address: "tcp://10.0.0.2:7101".to_string(),
session_id: "sess-9".to_string(),
spec_digest: "d".repeat(64),
engine_version: "0.7.22".to_string(),
launch_outcome: MaterializeLaunchOutcome::ResumedFromSnapshot,
resolved_auth_binding: None,
}),
BridgeReply::MemberReleased(BridgeMemberReleasedResponse {
disposal: MemberSessionDisposal::RuntimeReleasedOnly {
cause: RuntimeReleaseCause::NoDurableSessions,
},
}),
BridgeReply::HostStatus(BridgeHostStatusResponse {
runtime_incarnation: BridgeHostRuntimeIncarnation::from_uuid(uuid::uuid!(
"11111111-2222-4333-8444-555555555555"
)),
members: vec![BridgeHostMemberRecord {
agent_identity: "worker-1".to_string(),
generation: 1,
fence_token: 3,
session_id: "sess-9".to_string(),
spec_digest: "d".repeat(64),
healthy: true,
}],
capabilities,
}),
BridgeReply::MemberLiveChannelOpened(BridgeLiveOpenedResponse {
open: LiveOpenResult {
channel_id: "chan-1".to_string(),
transport: crate::wire::WireLiveTransportBootstrap::Websocket {
url: "wss://host-b:7443/live/chan-1".to_string(),
token: "one-time-token".to_string(),
},
capabilities: crate::wire::WireLiveChannelCapabilities {
audio_in: true,
audio_out: true,
text_in: true,
text_out: true,
image_in: false,
video_in: false,
transcript_supported: true,
barge_in_supported: true,
provider_native_resume: false,
},
continuity: crate::wire::WireLiveContinuityMode::Fresh,
},
}),
BridgeReply::MemberLiveChannelStatusReport {
channel_id: "chan-1".to_string(),
status: WireLiveAdapterStatus::Ready,
},
BridgeReply::MemberLiveChannelClosed {
status: crate::wire::LiveCloseStatus::Closed,
},
BridgeReply::MemberLiveChannelControlled(BridgeLiveControlledResponse {
outcome: BridgeLiveControlOutcome::Truncate {
status: crate::wire::LiveTruncateStatus::Truncated,
},
}),
BridgeReply::MemberOperatorReply(MemberOperatorReply {
request_id: "req-1".to_string(),
outcome: MemberOperatorOutcome::Completed {
result: WireOpaqueJson::from_value(
&json!({"member_id": "worker-2", "status": "active"}),
),
},
}),
BridgeReply::MemberOperatorReply(MemberOperatorReply {
request_id: "req-2".to_string(),
outcome: MemberOperatorOutcome::Rejected {
cause: BridgeRejectionCause::ScopeDenied {
required: crate::wire::mob::WireControlScope::SendCommand,
presented: vec![],
},
reason: "spawn scope not granted".to_string(),
},
}),
];
for reply in &replies {
assert_reply_value_round_trip(reply);
}
}
#[test]
fn v4_prepublication_runtime_incarnation_clean_cut_fails_closed_both_directions() {
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct PreIncarnationHostStatus {
members: Vec<BridgeHostMemberRecord>,
capabilities: BridgeCapabilities,
}
#[allow(dead_code)]
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct PreIncarnationMemberEventsPage {
generation: u64,
fence_token: u64,
events: Vec<WireEventRow>,
from_seq: u64,
next_seq: u64,
watermark: u64,
turn_outcomes: Vec<BridgeTurnOutcomeRecord>,
outcomes_complete: bool,
}
fn without_tag(mut value: serde_json::Value) -> serde_json::Value {
value
.as_object_mut()
.expect("reply is an object")
.remove("result")
.expect("reply carries its result tag");
value
}
let runtime_incarnation = BridgeHostRuntimeIncarnation::from_uuid(uuid::uuid!(
"aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"
));
let host_status = BridgeReply::HostStatus(BridgeHostStatusResponse {
runtime_incarnation,
members: Vec::new(),
capabilities: BridgeCapabilities::default(),
});
let host_status_value = serde_json::to_value(host_status).expect("serialize host status");
let mut pre_field_host_status = host_status_value.clone();
pre_field_host_status
.as_object_mut()
.expect("host status object")
.remove("runtime_incarnation");
let new_receiver_error = serde_json::from_value::<BridgeReply>(pre_field_host_status)
.expect_err("new V4 receiver rejects old HostStatus without the boot token");
assert!(
new_receiver_error
.to_string()
.contains("runtime_incarnation")
);
let old_receiver_error =
serde_json::from_value::<PreIncarnationHostStatus>(without_tag(host_status_value))
.expect_err("old strict V4 receiver rejects new HostStatus boot token");
assert!(
old_receiver_error
.to_string()
.contains("runtime_incarnation")
);
let member_events = BridgeReply::MemberEventsPage(BridgeMemberEventsPage {
runtime_incarnation,
generation: 3,
fence_token: 5,
events: Vec::new(),
from_seq: 1,
next_seq: 1,
watermark: 0,
turn_outcomes: Vec::new(),
outcomes_complete: true,
});
let member_events_value =
serde_json::to_value(member_events).expect("serialize member events page");
let mut pre_field_member_events = member_events_value.clone();
pre_field_member_events
.as_object_mut()
.expect("member events object")
.remove("runtime_incarnation");
let new_receiver_error = serde_json::from_value::<BridgeReply>(pre_field_member_events)
.expect_err("new V4 receiver rejects old MemberEventsPage without the boot token");
assert!(
new_receiver_error
.to_string()
.contains("runtime_incarnation")
);
let old_receiver_error = serde_json::from_value::<PreIncarnationMemberEventsPage>(
without_tag(member_events_value),
)
.expect_err("old strict V4 receiver rejects new MemberEventsPage boot token");
assert!(
old_receiver_error
.to_string()
.contains("runtime_incarnation")
);
}
#[test]
fn host_binding_descriptor_round_trips_and_fails_closed() {
let descriptor = WireHostBindingDescriptor {
kind: WireHostBindingDescriptorKind::Host,
address: "tcp://10.0.0.2:7100".to_string(),
identity: crate::wire::mob::WireTrustedPeerIdentity::Ed25519PublicKey {
public_key: "ed25519:BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwc=".to_string(),
},
bootstrap_token: "host-bootstrap".into(),
live_endpoint: None,
};
let value = serde_json::to_value(&descriptor).expect("serialize descriptor");
assert_eq!(value["kind"], json!("host"));
assert!(
value.get("live_endpoint").is_none(),
"absent live endpoint must be omitted"
);
let decoded: WireHostBindingDescriptor =
serde_json::from_value(value.clone()).expect("decode descriptor");
assert_eq!(decoded, descriptor);
let mut with_raw_peer = value;
with_raw_peer["peer_id"] = json!("raw-peer-id");
assert!(
serde_json::from_value::<WireHostBindingDescriptor>(with_raw_peer).is_err(),
"raw peer ids are not descriptor vocabulary — identity is the typed key"
);
}
#[test]
fn wire_opaque_json_round_trips_and_parses_back() {
let value = json!({"member_id": "worker-2", "status": "active"});
let envelope = WireOpaqueJson::from_value(&value);
let wire = serde_json::to_value(&envelope).expect("serialize envelope");
assert!(wire.is_string(), "envelope must be a transparent string");
let decoded: WireOpaqueJson = serde_json::from_value(wire).expect("decode envelope");
assert_eq!(decoded, envelope);
assert_eq!(decoded.to_value().expect("parse envelope body"), value);
let garbage: WireOpaqueJson =
serde_json::from_value(json!("not json at all")).expect("string decodes");
assert!(
garbage.to_value().is_err(),
"non-JSON envelope bodies must fail typed at parse time"
);
}
#[test]
fn wire_event_row_equality_is_serialized_form_equality() {
let row = WireEventRow {
durable_seq: 41,
envelope: meerkat_core::EventEnvelope::new(
"worker-1",
9,
Some("mob-1".to_string()),
meerkat_core::AgentEvent::TurnStarted { turn_number: 2 },
),
};
let wire = serde_json::to_value(&row).expect("serialize row");
assert!(
wire.get("durable_seq") == Some(&json!(41))
&& wire.get("envelope").and_then(|value| value.get("seq")) == Some(&json!(9)),
"row must keep the durable and source-stream seq domains distinct: {wire}"
);
let decoded: WireEventRow = serde_json::from_value(wire).expect("decode row");
assert_eq!(decoded, row, "round-tripped row must compare equal");
let other = WireEventRow {
durable_seq: 42,
envelope: row.envelope.clone(),
};
assert_ne!(row, other, "different durable seq must compare unequal");
}
}