mj_controller/session_manager/
handle.rs1use super::*;
2
3#[derive(Clone)]
4pub struct SessionManagerControl {
5 pub(super) commands: mpsc::Sender<ManagerCommand>,
6}
7
8#[derive(Clone, Debug)]
9pub struct ManagedSessionHandle {
10 pub(super) session_id: String,
11 pub(super) commands: mpsc::Sender<ActorCommand>,
12 pub(super) releases: mpsc::UnboundedSender<ReturnedConnection>,
13 pub(super) view: watch::Receiver<ManagedSessionView>,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct ReviewDeliveryAdmission {
21 pub(super) session_id: String,
22 pub(super) epoch: u64,
23 pub(super) command_id: String,
24}
25
26impl ReviewDeliveryAdmission {
27 pub(crate) fn new(session_id: String, epoch: u64, command_id: String) -> Self {
28 Self {
29 session_id,
30 epoch,
31 command_id,
32 }
33 }
34
35 pub(crate) fn session_id(&self) -> &str {
36 &self.session_id
37 }
38
39 pub(crate) const fn epoch(&self) -> u64 {
40 self.epoch
41 }
42
43 pub(crate) fn command_id(&self) -> &str {
44 &self.command_id
45 }
46}
47
48pub struct ManagedSessionLease {
58 pub(super) session_id: String,
59 pub(super) lease_id: Option<u64>,
60 pub(super) connection: Option<StandaloneSession>,
61 pub(super) releases: mpsc::UnboundedSender<ReturnedConnection>,
62}
63
64impl ManagedSessionLease {
65 pub fn connection_mut(&mut self) -> &mut StandaloneSession {
66 self.connection
67 .as_mut()
68 .expect("managed session lease has already been released")
69 }
70
71 pub fn replace_connection(&mut self, connection: StandaloneSession) {
74 drop(self.connection.take());
75 self.connection = Some(connection);
76 }
77
78 pub fn release(mut self) {
79 let lease_id = self
80 .lease_id
81 .take()
82 .expect("managed session lease has already been released");
83 let connection = self.connection.take();
84 if let Err(error) = self.releases.send(ReturnedConnection {
85 lease_id,
86 connection,
87 }) {
88 tracing::warn!(
89 session_id = %self.session_id,
90 operation = "lease_release",
91 %error,
92 "session actor stopped before receiving released relay connection"
93 );
94 }
95 }
96}
97
98impl Drop for ManagedSessionLease {
99 fn drop(&mut self) {
100 let Some(lease_id) = self.lease_id.take() else {
101 return;
102 };
103 drop(self.connection.take());
106 if let Err(error) = self.releases.send(ReturnedConnection {
107 lease_id,
108 connection: None,
109 }) {
110 tracing::warn!(
111 session_id = %self.session_id,
112 operation = "lease_drop",
113 %error,
114 "session actor stopped before receiving dropped relay lease"
115 );
116 }
117 }
118}
119
120impl ManagedSessionHandle {
121 pub fn client(&self) -> mj_client::session::SessionHandle {
123 mj_client::session::SessionHandle::new(ClientSessionHandle(self.clone()))
124 }
125
126 pub fn session_id(&self) -> &str {
127 &self.session_id
128 }
129
130 pub fn view(&self) -> ManagedSessionView {
131 self.view.borrow().clone()
132 }
133
134 pub fn is_stopped(&self) -> bool {
138 self.commands.is_closed()
139 }
140
141 pub fn has_changed(&self) -> Result<bool> {
142 self.view.has_changed().context("session manager stopped")
143 }
144
145 pub async fn changed(&mut self) -> Result<ManagedSessionView> {
146 self.view
147 .changed()
148 .await
149 .context("session manager stopped")?;
150 Ok(self.view())
151 }
152
153 pub async fn submit(&self, command_id: String, command: RelayCommand) -> Result<u64> {
154 self.enqueue_submit(command_id, command).await?.wait().await
155 }
156
157 pub(crate) async fn submit_review_delivery(
161 &self,
162 admission: ReviewDeliveryAdmission,
163 command: RelayCommand,
164 ) -> Result<u64> {
165 let command_id = admission.command_id.clone();
166 self.enqueue_submit_with_admission(command_id, command, Some(admission))
167 .await?
168 .wait()
169 .await
170 }
171
172 pub async fn enqueue_submit(
173 &self,
174 command_id: String,
175 command: RelayCommand,
176 ) -> Result<PendingRelaySubmit> {
177 self.enqueue_submit_with_admission(command_id, command, None)
178 .await
179 }
180
181 pub(super) async fn enqueue_submit_with_admission(
182 &self,
183 command_id: String,
184 command: RelayCommand,
185 admission: Option<ReviewDeliveryAdmission>,
186 ) -> Result<PendingRelaySubmit> {
187 let (reply, response) = oneshot::channel();
188 self.commands
189 .send(ActorCommand::Submit {
190 command_id,
191 command,
192 admission,
193 reply,
194 })
195 .await
196 .context("session manager stopped")?;
197 Ok(PendingRelaySubmit { response })
198 }
199
200 pub async fn sync_now(&self) -> Result<()> {
201 self.enqueue_sync().await?.wait().await
202 }
203
204 pub async fn respond_elicitation(
205 &self,
206 elicitation_id: String,
207 response: ElicitationResponse,
208 ) -> Result<()> {
209 let (reply, result) = oneshot::channel();
210 self.commands
211 .send(ActorCommand::RespondElicitation {
212 elicitation_id,
213 response,
214 reply,
215 })
216 .await
217 .context("session manager stopped")?;
218 result
219 .await
220 .context("session manager stopped")?
221 .map_err(anyhow::Error::msg)
222 }
223
224 pub async fn stop_background_task(&self, background_task_id: String) -> Result<()> {
225 let (reply, result) = oneshot::channel();
226 self.commands
227 .send(ActorCommand::StopBackgroundTask {
228 background_task_id,
229 reply,
230 })
231 .await
232 .context("session manager stopped")?;
233 result
234 .await
235 .context("session manager stopped")?
236 .map_err(anyhow::Error::msg)
237 }
238
239 pub async fn install_prompt_context(&self, text: String) -> Result<()> {
242 let (reply, result) = oneshot::channel();
243 self.commands
244 .send(ActorCommand::InstallPromptContext { text, reply })
245 .await
246 .context("session manager stopped")?;
247 result
248 .await
249 .context("session manager stopped")?
250 .map_err(anyhow::Error::msg)
251 }
252
253 pub async fn reviewer(&self, action: ReviewerAction) -> Result<ReviewerOutcome> {
259 self.reviewer_as(None, action).await
260 }
261
262 pub async fn reviewer_as(
266 &self,
267 role: Option<String>,
268 action: ReviewerAction,
269 ) -> Result<ReviewerOutcome> {
270 let (reply, result) = oneshot::channel();
271 self.commands
272 .send(ActorCommand::Reviewer {
273 role,
274 action,
275 reply,
276 })
277 .await
278 .context("session manager stopped")?;
279 result
280 .await
281 .context("session manager stopped")?
282 .map_err(anyhow::Error::msg)
283 }
284
285 pub async fn enqueue_sync(&self) -> Result<PendingRelaySync> {
286 let (reply, response) = oneshot::channel();
287 self.commands
288 .send(ActorCommand::Sync { reply })
289 .await
290 .context("session manager stopped")?;
291 Ok(PendingRelaySync { response })
292 }
293
294 pub async fn lease_connection(&self) -> Result<ManagedSessionLease> {
295 let (reply, response) = oneshot::channel();
296 self.commands
297 .send(ActorCommand::Lease { reply })
298 .await
299 .context("session manager stopped")?;
300 let (lease_id, connection) = response.await.context("session manager stopped")??;
301 Ok(ManagedSessionLease {
302 session_id: self.session_id.clone(),
303 lease_id: Some(lease_id),
304 connection: Some(connection),
305 releases: self.releases.clone(),
306 })
307 }
308}
309
310pub struct PendingRelaySubmit {
311 pub(super) response: oneshot::Receiver<std::result::Result<u64, String>>,
312}
313
314impl PendingRelaySubmit {
315 pub async fn wait(self) -> Result<u64> {
316 self.response
317 .await
318 .context("session manager stopped")?
319 .map_err(anyhow::Error::msg)
320 }
321}
322
323pub struct PendingRelaySync {
324 pub(super) response: oneshot::Receiver<std::result::Result<(), String>>,
325}
326
327impl PendingRelaySync {
328 pub async fn wait(self) -> Result<()> {
329 self.response
330 .await
331 .context("session manager stopped")?
332 .map_err(anyhow::Error::msg)
333 }
334}