1use std::io;
2use std::sync::Arc;
3
4use crate::session::task::TaskList;
5use crate::session::types::Session;
6use crate::session::PermissionAuditSeed;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum RetrievalWindowCheckpointOutcome {
15 Committed,
16 Rebased,
17}
18
19#[derive(Debug, Clone)]
23pub struct InheritedChildWait {
24 session_id: String,
25 created_at: chrono::DateTime<chrono::Utc>,
26 wait: crate::WaitingForChildrenState,
27}
28
29impl InheritedChildWait {
30 pub fn capture(session: &Session) -> Option<Self> {
31 let wait = session
32 .agent_runtime_state
33 .as_ref()?
34 .waiting_for_children
35 .as_ref()?;
36 if wait.registered_by_tool_call_id.is_some() {
37 return None;
38 }
39 Some(Self {
40 session_id: session.id.clone(),
41 created_at: session.created_at,
42 wait: wait.clone(),
43 })
44 }
45
46 pub fn session_id(&self) -> &str {
47 &self.session_id
48 }
49
50 pub fn wait(&self) -> &crate::WaitingForChildrenState {
51 &self.wait
52 }
53
54 pub fn validate_session(&self, session: &Session) -> io::Result<()> {
55 if session.id != self.session_id || session.created_at != self.created_at {
56 return Err(io::Error::other(crate::SessionAuthorityConflict(
57 "inherited child wait session changed identity or birth".into(),
58 )));
59 }
60 Ok(())
61 }
62}
63
64#[derive(Debug, Default)]
67pub struct RootInboxAdmission {
68 pub merged: usize,
69 pub committed_messages: Vec<crate::Message>,
70 pub admission_error: Option<String>,
71}
72
73pub fn append_missing_runtime_messages(session: &mut Session, durable: &Session) -> usize {
83 let mut seen = durable
84 .messages
85 .iter()
86 .map(|message| message.id.clone())
87 .collect::<std::collections::HashSet<_>>();
88 let missing = session
89 .messages
90 .iter()
91 .filter(|message| seen.insert(message.id.clone()))
92 .cloned()
93 .collect::<Vec<_>>();
94 let appended = missing.len();
95 session.messages = durable.messages.iter().cloned().chain(missing).collect();
96 session.merge_provider_transcript_from_durable(durable);
100 appended
101}
102
103pub fn merge_session_inbox_admission(session: &mut Session, durable: &Session) {
108 let Some(durable_state) = durable.session_inbox_admission().cloned() else {
109 return;
110 };
111 session
112 .session_inbox_admission_mut()
113 .merge_from(&durable_state);
114}
115
116pub fn restore_missing_admitted_inbox_messages(session: &mut Session, durable: &Session) -> usize {
128 let admission = durable.session_inbox_admission();
129 let mut restored = 0;
130 for (durable_index, message) in durable.messages.iter().enumerate() {
131 let typed_marker = message
132 .metadata
133 .as_ref()
134 .and_then(|metadata| metadata.get("session_message"))
135 .is_some_and(|marker| {
136 marker.get("id").and_then(serde_json::Value::as_str) == Some(message.id.as_str())
137 && marker
138 .get("target_session_id")
139 .and_then(serde_json::Value::as_str)
140 == Some(durable.id.as_str())
141 && crate::SessionMessageId::parse(message.id.clone()).is_ok()
142 });
143 let recent_cursor = admission.is_some_and(|state| state.contains_str(&message.id));
144 if !(typed_marker || recent_cursor)
145 || session
146 .messages
147 .iter()
148 .any(|current| current.id == message.id)
149 {
150 continue;
151 }
152
153 let insertion = durable.messages[..durable_index]
154 .iter()
155 .rev()
156 .find_map(|predecessor| {
157 session
158 .messages
159 .iter()
160 .position(|current| current.id == predecessor.id)
161 .map(|index| index + 1)
162 })
163 .or_else(|| {
164 durable.messages[durable_index + 1..]
165 .iter()
166 .find_map(|successor| {
167 session
168 .messages
169 .iter()
170 .position(|current| current.id == successor.id)
171 })
172 })
173 .unwrap_or(session.messages.len());
174 session.messages.insert(insertion, message.clone());
175 restored += 1;
176 }
177 restored
178}
179
180#[async_trait::async_trait]
187pub trait RuntimeSessionPersistence: Send + Sync {
188 fn inherited_child_wait(&self) -> Option<InheritedChildWait> {
190 None
191 }
192
193 fn bind_inherited_child_wait(
196 &self,
197 _inherited: InheritedChildWait,
198 ) -> io::Result<Arc<dyn RuntimeSessionPersistence>> {
199 Err(io::Error::new(
200 io::ErrorKind::Unsupported,
201 crate::SessionAuthorityConflict(
202 "execution-scoped inherited child wait persistence is unsupported".into(),
203 ),
204 ))
205 }
206
207 fn root_actor_writer(&self) -> Option<crate::RootActorRuntimeWrite> {
210 None
211 }
212
213 fn bind_root_inbox_receipt(
217 &self,
218 _receipt: crate::SessionInboxReceipt,
219 ) -> io::Result<Arc<dyn RuntimeSessionPersistence>> {
220 Err(io::Error::new(
221 io::ErrorKind::Unsupported,
222 "Root receipt binding unsupported",
223 ))
224 }
225
226 fn root_inbox_receipt(&self) -> Option<&crate::SessionInboxReceipt> {
227 None
228 }
229
230 fn root_actor_execution_required(&self, _session: &Session) -> bool {
234 false
235 }
236
237 async fn bind_root_actor_execution(
241 &self,
242 _session: &Session,
243 _run_id: &str,
244 ) -> io::Result<Option<RootActorExecutionBinding>> {
245 Ok(None)
246 }
247
248 async fn bind_root_actor_compaction(
251 &self,
252 _session: &Session,
253 _run_id: &str,
254 ) -> io::Result<Option<RootActorExecutionBinding>> {
255 Ok(None)
256 }
257
258 async fn save_runtime_session(&self, session: &mut Session) -> io::Result<()>;
260
261 fn supports_bound_startup_session(&self) -> bool {
263 false
264 }
265
266 async fn save_bound_startup_session(
270 &self,
271 _session: &mut Session,
272 _expected: &Session,
273 ) -> io::Result<()> {
274 Err(io::Error::new(
275 io::ErrorKind::Unsupported,
276 crate::SessionAuthorityConflict(
277 "conditional bound startup persistence is unsupported".into(),
278 ),
279 ))
280 }
281
282 async fn save_finalized_runtime_session(&self, session: &mut Session) -> io::Result<()> {
288 self.save_runtime_session(session).await
289 }
290
291 async fn save_finalized_runtime_with_inherited_child_wait(
295 &self,
296 session: &mut Session,
297 inherited: &crate::session::runtime_state::WaitingForChildrenState,
298 ) -> io::Result<()> {
299 let _ = (session, inherited);
300 Err(io::Error::new(
301 io::ErrorKind::Unsupported,
302 crate::SessionAuthorityConflict(
303 "atomic inherited child wait finalization is unsupported".into(),
304 ),
305 ))
306 }
307
308 async fn seed_runtime_activation(&self, _session: &mut Session) -> io::Result<()> {
321 Err(io::Error::new(
322 io::ErrorKind::Unsupported,
323 "runtime persistence does not support authoritative activation seeding",
324 ))
325 }
326
327 async fn record_permission_posture_activation(
336 &self,
337 _session_id: &str,
338 _expected_audit_revision: Option<u64>,
339 _seed: &PermissionAuditSeed,
340 ) -> io::Result<Option<Session>> {
341 Err(io::Error::new(
342 io::ErrorKind::Unsupported,
343 "runtime persistence does not support atomic permission posture activation",
344 ))
345 }
346
347 async fn save_runtime_control_plane(&self, session: &mut Session) -> io::Result<()> {
360 self.save_runtime_session(session).await
361 }
362
363 async fn load_runtime_control_plane(&self, session_id: &str) -> io::Result<Option<Session>> {
372 self.load_runtime_session(session_id).await
373 }
374
375 async fn update_task_list_control_plane(
387 &self,
388 session_id: &str,
389 task_list: &TaskList,
390 version: &str,
391 ) -> io::Result<bool> {
392 let Some(mut session) = self.load_runtime_session(session_id).await? else {
393 return Ok(false);
394 };
395 session.set_task_list(task_list.clone());
396 session.set_task_list_version_meta(version.to_string());
397 self.save_runtime_control_plane(&mut session).await?;
398 Ok(true)
399 }
400
401 async fn update_task_list_control_plane_if_version(
409 &self,
410 session_id: &str,
411 expected_version: &str,
412 expected_task_list: &TaskList,
413 task_list: &TaskList,
414 version: &str,
415 ) -> io::Result<bool> {
416 let _ = (
417 session_id,
418 expected_version,
419 expected_task_list,
420 task_list,
421 version,
422 );
423 Ok(false)
424 }
425
426 async fn update_task_list_control_planes_if_version(
435 &self,
436 session_id: &str,
437 shared_session_id: &str,
438 expected_version: &str,
439 expected_task_list: &TaskList,
440 task_list: &TaskList,
441 version: &str,
442 ) -> io::Result<bool> {
443 if session_id == shared_session_id {
444 return self
445 .update_task_list_control_plane_if_version(
446 session_id,
447 expected_version,
448 expected_task_list,
449 task_list,
450 version,
451 )
452 .await;
453 }
454 let _ = (
455 session_id,
456 shared_session_id,
457 expected_version,
458 expected_task_list,
459 task_list,
460 version,
461 );
462 Ok(false)
463 }
464
465 async fn checkpoint_runtime_session(&self, session: &mut Session) -> io::Result<()> {
475 if let Some(durable) = self.load_runtime_session(&session.id).await? {
476 session
477 .check_conversation_generation(&durable)
478 .map_err(|error| {
479 io::Error::new(
480 io::ErrorKind::WouldBlock,
481 crate::SessionAuthorityConflict(error.to_string()),
482 )
483 })?;
484 append_missing_runtime_messages(session, &durable);
485 merge_session_inbox_admission(session, &durable);
486 }
487 self.save_runtime_session(session).await
488 }
489
490 async fn checkpoint_live_bootstrap(
493 &self,
494 _session: &Session,
495 _inbox: Arc<dyn crate::SessionInboxPort>,
496 _gate: crate::AdmissionGate,
497 ) -> io::Result<crate::LiveBootstrapProof> {
498 Err(io::Error::new(
499 io::ErrorKind::Unsupported,
500 "live bootstrap checkpoint unsupported",
501 ))
502 }
503
504 async fn confirm_live_bootstrap(
505 &self,
506 _proof: &crate::LiveBootstrapProof,
507 _inbox: Arc<dyn crate::SessionInboxPort>,
508 _publish: crate::RootActorRuntimeEventPublisher,
509 ) -> io::Result<()> {
510 Err(io::Error::new(
511 io::ErrorKind::Unsupported,
512 "live bootstrap confirmation unsupported",
513 ))
514 }
515
516 async fn claim_scoped_loop_controls(
519 &self,
520 _session: &Session,
521 _inbox: Arc<dyn crate::SessionInboxPort>,
522 _active_run_id: &str,
523 _limit: usize,
524 ) -> io::Result<Vec<crate::ScopedLoopControlClaim>> {
525 Err(io::Error::new(
526 io::ErrorKind::Unsupported,
527 "scoped Loop claims unsupported",
528 ))
529 }
530
531 async fn checkpoint_scoped_loop_control(
534 &self,
535 _session: &mut Session,
536 _bindings: &[crate::ScopedLoopControlBinding],
537 _activation: Arc<dyn crate::SessionActivationPort>,
538 _input: Option<(
539 Arc<dyn crate::SessionInboxPort>,
540 crate::ScopedLoopControlClaim,
541 )>,
542 ) -> io::Result<()> {
543 Err(io::Error::new(
544 io::ErrorKind::Unsupported,
545 "scoped Loop checkpoint unsupported",
546 ))
547 }
548
549 async fn load_loop_control_checkpoint(&self, _session_id: &str) -> io::Result<Option<Session>> {
552 Err(io::Error::new(
553 io::ErrorKind::Unsupported,
554 "strict Loop checkpoint reload unsupported",
555 ))
556 }
557
558 async fn admit_root_inbox(
559 &self,
560 _session: &mut Session,
561 _inbox: Arc<dyn crate::SessionInboxPort>,
562 _active_run_id: Option<&str>,
563 ) -> io::Result<Option<RootInboxAdmission>> {
564 Ok(None)
565 }
566
567 async fn checkpoint_retrieval_window(
584 &self,
585 _expected_base: &Session,
586 _staged: &mut Session,
587 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
588 Err(io::Error::new(
589 io::ErrorKind::Unsupported,
590 "runtime persistence does not support retrieval-window checkpoints",
591 ))
592 }
593
594 async fn checkpoint_prompt_rewrite(
603 &self,
604 _expected_base: &Session,
605 _staged: &mut Session,
606 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
607 Err(io::Error::new(
608 io::ErrorKind::Unsupported,
609 "runtime persistence does not support prompt-rewrite checkpoints",
610 ))
611 }
612
613 async fn checkpoint_manual_archive_rejection(
621 &self,
622 _expected_base: &Session,
623 _staged: &mut Session,
624 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
625 Err(io::Error::new(
626 io::ErrorKind::Unsupported,
627 "runtime persistence does not support manual archive-rejection checkpoints",
628 ))
629 }
630
631 async fn checkpoint_manual_archive_consumption(
639 &self,
640 _expected_base: &Session,
641 _staged: &mut Session,
642 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
643 Err(io::Error::new(
644 io::ErrorKind::Unsupported,
645 "runtime persistence does not support manual archive-consumption checkpoints",
646 ))
647 }
648
649 async fn load_runtime_session(&self, _session_id: &str) -> io::Result<Option<Session>> {
654 Ok(None)
655 }
656
657 async fn load_runtime_admission_snapshot(
663 &self,
664 session_id: &str,
665 ) -> io::Result<Option<Session>> {
666 self.load_runtime_session(session_id).await
667 }
668
669 async fn clear_legacy_pending_messages(
673 &self,
674 session_id: &str,
675 expected: &[serde_json::Value],
676 ) -> io::Result<bool> {
677 let Some(mut latest) = self.load_runtime_session(session_id).await? else {
678 return Ok(false);
679 };
680 if latest.pending_injected_messages().as_deref() != Some(expected) {
681 return Ok(false);
682 }
683 latest.clear_pending_injected_messages();
684 self.save_runtime_session(&mut latest).await?;
685 Ok(true)
686 }
687
688 async fn append_token_usage_record(&self, session_id: &str, json_line: &str) -> io::Result<()> {
694 let _ = (session_id, json_line);
695 Ok(())
696 }
697}
698
699pub struct RootActorExecutionBinding {
703 pub persistence: Arc<dyn RuntimeSessionPersistence>,
704 pub directory: Arc<dyn crate::ActorDirectoryPort>,
705 pub owner: crate::RootActorRuntimeWrite,
706 pub lease_duration: std::time::Duration,
707 pub terminal_publisher: Option<crate::storage::RootActorRuntimePublisher>,
709 abandon: Option<Box<dyn FnOnce() + Send + Sync>>,
710}
711
712impl RootActorExecutionBinding {
713 pub fn new(
714 persistence: Arc<dyn RuntimeSessionPersistence>,
715 directory: Arc<dyn crate::ActorDirectoryPort>,
716 owner: crate::RootActorRuntimeWrite,
717 lease_duration: std::time::Duration,
718 abandon: impl FnOnce() + Send + Sync + 'static,
719 ) -> Self {
720 Self {
721 persistence,
722 directory,
723 owner,
724 lease_duration,
725 terminal_publisher: None,
726 abandon: Some(Box::new(abandon)),
727 }
728 }
729
730 pub fn with_terminal_publisher(
731 mut self,
732 publish: crate::storage::RootActorRuntimePublisher,
733 ) -> Self {
734 self.terminal_publisher = Some(publish);
735 self
736 }
737
738 pub fn disarm_abandonment(&mut self) {
741 self.abandon.take();
742 }
743}
744
745impl Drop for RootActorExecutionBinding {
746 fn drop(&mut self) {
747 if let Some(abandon) = self.abandon.take() {
748 abandon();
749 }
750 }
751}
752
753#[async_trait::async_trait]
754impl<T: RuntimeSessionPersistence + ?Sized> RuntimeSessionPersistence for Arc<T> {
755 fn inherited_child_wait(&self) -> Option<InheritedChildWait> {
756 (**self).inherited_child_wait()
757 }
758
759 fn bind_inherited_child_wait(
760 &self,
761 inherited: InheritedChildWait,
762 ) -> io::Result<Arc<dyn RuntimeSessionPersistence>> {
763 (**self).bind_inherited_child_wait(inherited)
764 }
765
766 fn root_actor_writer(&self) -> Option<crate::RootActorRuntimeWrite> {
767 (**self).root_actor_writer()
768 }
769
770 fn bind_root_inbox_receipt(
771 &self,
772 receipt: crate::SessionInboxReceipt,
773 ) -> io::Result<Arc<dyn RuntimeSessionPersistence>> {
774 (**self).bind_root_inbox_receipt(receipt)
775 }
776
777 fn root_inbox_receipt(&self) -> Option<&crate::SessionInboxReceipt> {
778 (**self).root_inbox_receipt()
779 }
780
781 fn root_actor_execution_required(&self, session: &Session) -> bool {
782 (**self).root_actor_execution_required(session)
783 }
784
785 async fn bind_root_actor_execution(
786 &self,
787 session: &Session,
788 run_id: &str,
789 ) -> io::Result<Option<RootActorExecutionBinding>> {
790 (**self).bind_root_actor_execution(session, run_id).await
791 }
792
793 async fn bind_root_actor_compaction(
794 &self,
795 session: &Session,
796 run_id: &str,
797 ) -> io::Result<Option<RootActorExecutionBinding>> {
798 (**self).bind_root_actor_compaction(session, run_id).await
799 }
800
801 async fn save_runtime_session(&self, session: &mut Session) -> io::Result<()> {
802 (**self).save_runtime_session(session).await
803 }
804
805 fn supports_bound_startup_session(&self) -> bool {
806 (**self).supports_bound_startup_session()
807 }
808
809 async fn save_bound_startup_session(
810 &self,
811 session: &mut Session,
812 expected: &Session,
813 ) -> io::Result<()> {
814 (**self).save_bound_startup_session(session, expected).await
815 }
816
817 async fn save_finalized_runtime_session(&self, session: &mut Session) -> io::Result<()> {
818 (**self).save_finalized_runtime_session(session).await
819 }
820
821 async fn save_finalized_runtime_with_inherited_child_wait(
822 &self,
823 session: &mut Session,
824 inherited: &crate::session::runtime_state::WaitingForChildrenState,
825 ) -> io::Result<()> {
826 (**self)
827 .save_finalized_runtime_with_inherited_child_wait(session, inherited)
828 .await
829 }
830
831 async fn seed_runtime_activation(&self, session: &mut Session) -> io::Result<()> {
832 (**self).seed_runtime_activation(session).await
833 }
834
835 async fn record_permission_posture_activation(
836 &self,
837 session_id: &str,
838 expected_audit_revision: Option<u64>,
839 seed: &PermissionAuditSeed,
840 ) -> io::Result<Option<Session>> {
841 (**self)
842 .record_permission_posture_activation(session_id, expected_audit_revision, seed)
843 .await
844 }
845
846 async fn save_runtime_control_plane(&self, session: &mut Session) -> io::Result<()> {
847 (**self).save_runtime_control_plane(session).await
848 }
849
850 async fn load_runtime_control_plane(&self, session_id: &str) -> io::Result<Option<Session>> {
851 (**self).load_runtime_control_plane(session_id).await
852 }
853
854 async fn update_task_list_control_plane(
855 &self,
856 session_id: &str,
857 task_list: &TaskList,
858 version: &str,
859 ) -> io::Result<bool> {
860 (**self)
861 .update_task_list_control_plane(session_id, task_list, version)
862 .await
863 }
864
865 async fn update_task_list_control_plane_if_version(
866 &self,
867 session_id: &str,
868 expected_version: &str,
869 expected_task_list: &TaskList,
870 task_list: &TaskList,
871 version: &str,
872 ) -> io::Result<bool> {
873 (**self)
874 .update_task_list_control_plane_if_version(
875 session_id,
876 expected_version,
877 expected_task_list,
878 task_list,
879 version,
880 )
881 .await
882 }
883
884 async fn update_task_list_control_planes_if_version(
885 &self,
886 session_id: &str,
887 shared_session_id: &str,
888 expected_version: &str,
889 expected_task_list: &TaskList,
890 task_list: &TaskList,
891 version: &str,
892 ) -> io::Result<bool> {
893 (**self)
894 .update_task_list_control_planes_if_version(
895 session_id,
896 shared_session_id,
897 expected_version,
898 expected_task_list,
899 task_list,
900 version,
901 )
902 .await
903 }
904
905 async fn checkpoint_runtime_session(&self, session: &mut Session) -> io::Result<()> {
906 (**self).checkpoint_runtime_session(session).await
907 }
908
909 async fn checkpoint_live_bootstrap(
910 &self,
911 session: &Session,
912 inbox: Arc<dyn crate::SessionInboxPort>,
913 gate: crate::AdmissionGate,
914 ) -> io::Result<crate::LiveBootstrapProof> {
915 (**self)
916 .checkpoint_live_bootstrap(session, inbox, gate)
917 .await
918 }
919
920 async fn confirm_live_bootstrap(
921 &self,
922 proof: &crate::LiveBootstrapProof,
923 inbox: Arc<dyn crate::SessionInboxPort>,
924 publish: crate::RootActorRuntimeEventPublisher,
925 ) -> io::Result<()> {
926 (**self).confirm_live_bootstrap(proof, inbox, publish).await
927 }
928
929 async fn claim_scoped_loop_controls(
930 &self,
931 session: &Session,
932 inbox: Arc<dyn crate::SessionInboxPort>,
933 active_run_id: &str,
934 limit: usize,
935 ) -> io::Result<Vec<crate::ScopedLoopControlClaim>> {
936 (**self)
937 .claim_scoped_loop_controls(session, inbox, active_run_id, limit)
938 .await
939 }
940
941 async fn checkpoint_scoped_loop_control(
942 &self,
943 session: &mut Session,
944 bindings: &[crate::ScopedLoopControlBinding],
945 activation: Arc<dyn crate::SessionActivationPort>,
946 input: Option<(
947 Arc<dyn crate::SessionInboxPort>,
948 crate::ScopedLoopControlClaim,
949 )>,
950 ) -> io::Result<()> {
951 (**self)
952 .checkpoint_scoped_loop_control(session, bindings, activation, input)
953 .await
954 }
955
956 async fn load_loop_control_checkpoint(&self, session_id: &str) -> io::Result<Option<Session>> {
957 (**self).load_loop_control_checkpoint(session_id).await
958 }
959
960 async fn admit_root_inbox(
961 &self,
962 session: &mut Session,
963 inbox: Arc<dyn crate::SessionInboxPort>,
964 active_run_id: Option<&str>,
965 ) -> io::Result<Option<RootInboxAdmission>> {
966 (**self)
967 .admit_root_inbox(session, inbox, active_run_id)
968 .await
969 }
970
971 async fn checkpoint_retrieval_window(
972 &self,
973 expected_base: &Session,
974 staged: &mut Session,
975 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
976 (**self)
977 .checkpoint_retrieval_window(expected_base, staged)
978 .await
979 }
980
981 async fn checkpoint_prompt_rewrite(
982 &self,
983 expected_base: &Session,
984 staged: &mut Session,
985 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
986 (**self)
987 .checkpoint_prompt_rewrite(expected_base, staged)
988 .await
989 }
990
991 async fn checkpoint_manual_archive_rejection(
992 &self,
993 expected_base: &Session,
994 staged: &mut Session,
995 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
996 (**self)
997 .checkpoint_manual_archive_rejection(expected_base, staged)
998 .await
999 }
1000
1001 async fn checkpoint_manual_archive_consumption(
1002 &self,
1003 expected_base: &Session,
1004 staged: &mut Session,
1005 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
1006 (**self)
1007 .checkpoint_manual_archive_consumption(expected_base, staged)
1008 .await
1009 }
1010
1011 async fn load_runtime_session(&self, session_id: &str) -> io::Result<Option<Session>> {
1012 (**self).load_runtime_session(session_id).await
1013 }
1014
1015 async fn load_runtime_admission_snapshot(
1016 &self,
1017 session_id: &str,
1018 ) -> io::Result<Option<Session>> {
1019 (**self).load_runtime_admission_snapshot(session_id).await
1020 }
1021
1022 async fn clear_legacy_pending_messages(
1023 &self,
1024 session_id: &str,
1025 expected: &[serde_json::Value],
1026 ) -> io::Result<bool> {
1027 (**self)
1028 .clear_legacy_pending_messages(session_id, expected)
1029 .await
1030 }
1031
1032 async fn append_token_usage_record(&self, session_id: &str, json_line: &str) -> io::Result<()> {
1033 (**self)
1034 .append_token_usage_record(session_id, json_line)
1035 .await
1036 }
1037}