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