Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
525 changes: 525 additions & 0 deletions src/common/active_session_snapshot.rs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/common/feature_support.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::ActiveSessionSnapshotCapabilities;
use serde::{Deserialize, Serialize};

/// Client feature support declaration.
Expand All @@ -14,4 +15,7 @@ pub struct FeatureSupport {
/// Whether the client supports the "Full" role ACL.
#[serde(default)]
pub supports_full_role_for_real: bool,
/// Active-session snapshot protocol versions this client can use.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub active_session_snapshot: Option<ActiveSessionSnapshotCapabilities>,
}
2 changes: 2 additions & 0 deletions src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Common types used by both sharer and viewer.
mod active_session_snapshot;

mod agent_prompt;
mod command_execution;
Expand All @@ -19,6 +20,7 @@ mod ui_state;
mod user;
mod write_to_pty;

pub use active_session_snapshot::*;
pub use agent_prompt::*;
pub use command_execution::*;
pub use control_action::*;
Expand Down
44 changes: 36 additions & 8 deletions src/sharer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
//! since old clients may not specify new fields expected by the server.

use crate::common::{
ActivePrompt, ActivePromptUpdate, AgentPromptFailureReason, AgentPromptRequest,
AgentPromptRequestId, BlockId, BufferId, CommandExecutionFailureReason,
CommandExecutionRequestId, ControlAction, ControlActionFailureReason, ControlActionRequestId,
FeatureSupport, InputOperationId, InputReplicaId, InputUpdate, InputUpdateFailureReason,
OrderedTerminalEvent, ParticipantId, ParticipantList, ParticipantPresenceUpdate, Role,
RoleRequestId, RoleRequestResponse, Selection, SelectionUpdate, SessionId, SessionSecret,
TelemetryContext, UniversalDeveloperInputContext, UniversalDeveloperInputContextUpdate, UserID,
WindowSize, WriteToPtyFailureReason, WriteToPtyRequestId,
ActivePrompt, ActivePromptUpdate, ActiveSessionSnapshotPublicationAck,
AgentPromptFailureReason, AgentPromptRequest, AgentPromptRequestId, BlockId, BufferId,
CommandExecutionFailureReason, CommandExecutionRequestId, ControlAction,
ControlActionFailureReason, ControlActionRequestId, FeatureSupport, InputOperationId,
InputReplicaId, InputUpdate, InputUpdateFailureReason, NegotiatedActiveSessionSnapshotProtocol,
OrderedTerminalEvent, ParticipantId, ParticipantList, ParticipantPresenceUpdate,
PreparedActiveSessionSnapshotReceipt, Role, RoleRequestId, RoleRequestResponse, Selection,
SelectionUpdate, SessionId, SessionSecret, TelemetryContext, UniversalDeveloperInputContext,
UniversalDeveloperInputContextUpdate, UserID, WindowSize, WriteToPtyFailureReason,
WriteToPtyRequestId,
};

use super::common::Scrollback;
Expand Down Expand Up @@ -368,6 +370,9 @@ pub enum DownstreamMessage {
sharer_id: ParticipantId,
/// The Firebase UID assigned to the sharer.
sharer_firebase_uid: String,
/// Snapshot protocol selected from the sharer's advertised capabilities.
#[serde(default, skip_serializing_if = "Option::is_none")]
active_session_snapshot_protocol: Option<NegotiatedActiveSessionSnapshotProtocol>,
},

/// The server denied the initialization request. No further messages will be processed.
Expand All @@ -385,6 +390,9 @@ pub enum DownstreamMessage {
/// The sharer can use this to update the server with any newer events created while disconnected.
last_received_event_no: Option<usize>,
participant_list: ParticipantList,
/// Snapshot protocol selected from the sharer's advertised capabilities.
#[serde(default, skip_serializing_if = "Option::is_none")]
active_session_snapshot_protocol: Option<NegotiatedActiveSessionSnapshotProtocol>,
},

/// The server denied the reconnection request. No further messages will be processed.
Expand All @@ -394,6 +402,9 @@ pub enum DownstreamMessage {
/// and the sharer can safely remove them from memory.
EventsProcessedAck { latest_processed_event_no: usize },

/// The server acknowledged an out-of-band snapshot publication attempt.
ActiveSessionSnapshotPublicationAck(ActiveSessionSnapshotPublicationAck),

/// Sent when the list of participants in the shared session changes.
ParticipantListUpdated(ParticipantList),

Expand Down Expand Up @@ -485,6 +496,18 @@ pub enum DownstreamMessage {
}

impl DownstreamMessage {
pub fn requires_active_session_snapshot_support(&self) -> bool {
matches!(
self,
Self::SessionInitialized {
active_session_snapshot_protocol: Some(_),
..
} | Self::SessionReconnected {
active_session_snapshot_protocol: Some(_),
..
} | Self::ActiveSessionSnapshotPublicationAck(_)
)
}
pub fn from_json(json: &str) -> serde_json::Result<Self> {
serde_json::from_str(json)
}
Expand Down Expand Up @@ -530,6 +553,11 @@ pub enum UpstreamMessage {
/// Sent when there is any ordered terminal event.
OrderedTerminalEvent(OrderedTerminalEvent),

/// Submits a storage-validated snapshot receipt for publication.
PublishActiveSessionSnapshot {
receipt: PreparedActiveSessionSnapshotReceipt,
},

/// Sent to reconnect to the server after disconnection.
Reconnect(ReconnectPayload),

Expand Down
56 changes: 44 additions & 12 deletions src/viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@

use crate::{
common::{
ActivePrompt, ActivePromptUpdate, AgentAttachment, AgentPromptFailureReason,
AgentPromptRequest, AgentPromptRequestId, BlockId, BufferId, CommandExecutionFailureReason,
CommandExecutionRequestId, ControlAction, ControlActionFailureReason, FeatureSupport,
InputOperationId, InputReplicaId, InputUpdate, InputUpdateFailureReason,
LinkAccessLevelUpdateResponse, OrderedTerminalEvent, ParticipantId, ParticipantList,
ParticipantPresenceUpdate, Role, RoleRequestId, RoleRequestResponse, Scrollback,
SelectionUpdate, TeamAccessLevelUpdateResponse, TeamAclData, TelemetryContext,
UniversalDeveloperInputContext, UniversalDeveloperInputContextUpdate, UserID, WindowSize,
WriteToPtyFailureReason, WriteToPtyRequestId,
ActivePrompt, ActivePromptUpdate, ActiveSessionSnapshotRestore,
ActiveSessionSnapshotResumeCursor, ActiveSessionSnapshotResyncReason, AgentAttachment,
AgentPromptFailureReason, AgentPromptRequest, AgentPromptRequestId, BlockId, BufferId,
CommandExecutionFailureReason, CommandExecutionRequestId, ControlAction,
ControlActionFailureReason, FeatureSupport, InputOperationId, InputReplicaId, InputUpdate,
InputUpdateFailureReason, LinkAccessLevelUpdateResponse, OrderedTerminalEvent,
ParticipantId, ParticipantList, ParticipantPresenceUpdate, Role, RoleRequestId,
RoleRequestResponse, Scrollback, SelectionUpdate, TeamAccessLevelUpdateResponse,
TeamAclData, TelemetryContext, UniversalDeveloperInputContext,
UniversalDeveloperInputContextUpdate, UserID, WindowSize, WriteToPtyFailureReason,
WriteToPtyRequestId,
},
sharer::{self, LegacySessionSourceType, SessionSourceType},
};
Expand Down Expand Up @@ -100,6 +102,10 @@ pub struct InitPayload {
/// Client feature support declaration.
#[serde(default)]
pub feature_support: FeatureSupport,

/// Snapshot identity and last contiguous event applied by a reconnecting viewer.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub active_session_snapshot_cursor: Option<ActiveSessionSnapshotResumeCursor>,
}

/// The possible messages sent from server to client (viewer).
Expand Down Expand Up @@ -149,11 +155,18 @@ pub enum DownstreamMessage {
/// off the source-type variant kind.
#[serde(default)]
source_task_id: Option<String>,

/// Negotiated snapshot bootstrap instructions. Absent for legacy replay.
#[serde(default, skip_serializing_if = "Option::is_none")]
active_session_snapshot_restore: Option<Box<ActiveSessionSnapshotRestore>>,
},

/// The server sends this message when the session was successfully rejoined.
RejoinedSuccessfully {
participant_list: Box<ParticipantList>,
/// Negotiated resume or fresh bootstrap instructions.
#[serde(default, skip_serializing_if = "Option::is_none")]
active_session_snapshot_restore: Option<Box<ActiveSessionSnapshotRestore>>,
},

/// Sent when the viewer fails to join the shared session.
Expand All @@ -174,6 +187,11 @@ pub enum DownstreamMessage {
/// These messages are only sent _after_ [`DownstreamMessage::JoinedSuccessfully`].
OrderedTerminalEvent(OrderedTerminalEvent),

/// The viewer must discard transient restore state and request a fresh bootstrap.
ActiveSessionSnapshotResyncRequired {
reason: ActiveSessionSnapshotResyncReason,
},

/// Sent when the list of participants in the shared session changes.
ParticipantListUpdated(ParticipantList),

Expand Down Expand Up @@ -269,6 +287,18 @@ pub enum DownstreamMessage {
}

impl DownstreamMessage {
pub fn requires_active_session_snapshot_support(&self) -> bool {
matches!(
self,
Self::JoinedSuccessfully {
active_session_snapshot_restore: Some(_),
..
} | Self::RejoinedSuccessfully {
active_session_snapshot_restore: Some(_),
..
} | Self::ActiveSessionSnapshotResyncRequired { .. }
)
}
pub fn from_json(json: &str) -> serde_json::Result<Self> {
serde_json::from_str(json)
}
Expand All @@ -285,9 +315,9 @@ impl DownstreamMessage {
Self::JoinedSuccessfully {
participant_list, ..
} => participant_list.downgrade_full_roles(),
Self::RejoinedSuccessfully { participant_list } => {
participant_list.downgrade_full_roles()
}
Self::RejoinedSuccessfully {
participant_list, ..
} => participant_list.downgrade_full_roles(),
Self::ParticipantListUpdated(list) => list.downgrade_full_roles(),
Self::ParticipantRoleChanged { role, .. } => role.downgrade_full(),
Self::RoleRequestResponse(RoleRequestResponse::Approved { new_role }) => {
Expand Down Expand Up @@ -319,6 +349,8 @@ impl DownstreamMessage {
}

/// The possible messages sent from client (viewer) to server.
// Boxing `Initialize` would be wire-compatible but would churn every legacy call site.
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Serialize, Deserialize)]
pub enum UpstreamMessage {
/// The client sends this message to join the shared session.
Expand Down
Loading