Skip to main content

lash_remote_protocol/protocol/
observations.rs

1#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2pub struct RemoteSessionCursor {
3    pub protocol_version: u32,
4    pub cursor: String,
5}
6
7impl RemoteSessionCursor {
8    pub fn new(cursor: impl Into<String>) -> Self {
9        Self {
10            protocol_version: REMOTE_PROTOCOL_VERSION,
11            cursor: cursor.into(),
12        }
13    }
14
15    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
16        ensure_protocol_version(self.protocol_version)?;
17        require_non_empty("RemoteSessionCursor", "cursor", &self.cursor)
18    }
19}
20
21#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
22pub struct RemoteSessionObservation {
23    pub protocol_version: u32,
24    pub session_id: String,
25    pub cursor: String,
26    pub turn_index: u64,
27    pub usage: RemoteUsage,
28}
29
30impl RemoteSessionObservation {
31    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
32        ensure_protocol_version(self.protocol_version)?;
33        require_non_empty("RemoteSessionObservation", "session_id", &self.session_id)?;
34        require_non_empty("RemoteSessionObservation", "cursor", &self.cursor)
35    }
36}
37
38#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
39pub struct RemoteSessionObservationEvent {
40    pub protocol_version: u32,
41    pub session_id: String,
42    pub revision: u64,
43    pub cursor: String,
44    #[serde(flatten)]
45    pub event: RemoteSessionObservationEventPayload,
46}
47
48impl RemoteSessionObservationEvent {
49    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
50        ensure_protocol_version(self.protocol_version)?;
51        require_non_empty(
52            "RemoteSessionObservationEvent",
53            "session_id",
54            &self.session_id,
55        )?;
56        require_non_empty("RemoteSessionObservationEvent", "cursor", &self.cursor)?;
57        if let RemoteSessionObservationEventPayload::TurnActivity { activity } = &self.event {
58            activity.validate()?;
59            if activity.protocol_version != self.protocol_version {
60                return Err(RemoteProtocolError::MismatchedNestedProtocolVersion {
61                    parent: "RemoteSessionObservationEvent",
62                    child: "activity",
63                    parent_version: self.protocol_version,
64                    child_version: activity.protocol_version,
65                });
66            }
67        }
68        Ok(())
69    }
70}
71
72#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
73#[serde(tag = "type", rename_all = "snake_case")]
74pub enum RemoteSessionObservationEventPayload {
75    TurnActivity {
76        activity: RemoteTurnActivity,
77    },
78    Committed,
79    AgentFrameSwitched {
80        frame_id: String,
81    },
82    QueueChanged {
83        kind: RemoteSessionQueueEventKind,
84        batch_ids: Vec<String>,
85    },
86    ProcessChanged {
87        kind: RemoteSessionProcessEventKind,
88        process_ids: Vec<String>,
89    },
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
93#[serde(rename_all = "snake_case")]
94pub enum RemoteSessionQueueEventKind {
95    Enqueued,
96    Cancelled,
97}
98
99#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
100#[serde(rename_all = "snake_case")]
101pub enum RemoteSessionProcessEventKind {
102    Started,
103    Cancelled,
104}
105
106#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
107pub struct RemoteLiveReplayGap {
108    pub protocol_version: u32,
109    pub session_id: String,
110    pub requested_cursor: String,
111    pub latest_cursor: String,
112    pub latest_revision: u64,
113    pub reason: RemoteLiveReplayGapReason,
114}
115
116impl RemoteLiveReplayGap {
117    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
118        ensure_protocol_version(self.protocol_version)?;
119        require_non_empty("RemoteLiveReplayGap", "session_id", &self.session_id)?;
120        require_non_empty(
121            "RemoteLiveReplayGap",
122            "requested_cursor",
123            &self.requested_cursor,
124        )?;
125        require_non_empty("RemoteLiveReplayGap", "latest_cursor", &self.latest_cursor)
126    }
127}
128
129#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
130#[serde(rename_all = "snake_case")]
131pub enum RemoteLiveReplayGapReason {
132    Trimmed,
133    Unavailable,
134}
135
136impl RemoteTurnResult {
137    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
138        ensure_protocol_version(self.protocol_version)?;
139        require_non_empty("RemoteTurnResult", "session_id", &self.session_id)?;
140        require_non_empty("RemoteTurnResult", "turn_id", &self.turn_id)?;
141        for activity in &self.activities {
142            if activity.protocol_version != self.protocol_version {
143                return Err(RemoteProtocolError::MismatchedNestedProtocolVersion {
144                    parent: "RemoteTurnResult",
145                    child: "activities",
146                    parent_version: self.protocol_version,
147                    child_version: activity.protocol_version,
148                });
149            }
150            activity.validate()?;
151        }
152        Ok(())
153    }
154}