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 save_runtime_session(&self, session: &mut Session) -> io::Result<()>;
250
251 fn supports_bound_startup_session(&self) -> bool {
253 false
254 }
255
256 async fn save_bound_startup_session(
260 &self,
261 _session: &mut Session,
262 _expected: &Session,
263 ) -> io::Result<()> {
264 Err(io::Error::new(
265 io::ErrorKind::Unsupported,
266 crate::SessionAuthorityConflict(
267 "conditional bound startup persistence is unsupported".into(),
268 ),
269 ))
270 }
271
272 async fn save_finalized_runtime_session(&self, session: &mut Session) -> io::Result<()> {
278 self.save_runtime_session(session).await
279 }
280
281 async fn save_finalized_runtime_with_inherited_child_wait(
285 &self,
286 session: &mut Session,
287 inherited: &crate::session::runtime_state::WaitingForChildrenState,
288 ) -> io::Result<()> {
289 let _ = (session, inherited);
290 Err(io::Error::new(
291 io::ErrorKind::Unsupported,
292 crate::SessionAuthorityConflict(
293 "atomic inherited child wait finalization is unsupported".into(),
294 ),
295 ))
296 }
297
298 async fn seed_runtime_activation(&self, _session: &mut Session) -> io::Result<()> {
311 Err(io::Error::new(
312 io::ErrorKind::Unsupported,
313 "runtime persistence does not support authoritative activation seeding",
314 ))
315 }
316
317 async fn record_permission_posture_activation(
326 &self,
327 _session_id: &str,
328 _expected_audit_revision: Option<u64>,
329 _seed: &PermissionAuditSeed,
330 ) -> io::Result<Option<Session>> {
331 Err(io::Error::new(
332 io::ErrorKind::Unsupported,
333 "runtime persistence does not support atomic permission posture activation",
334 ))
335 }
336
337 async fn save_runtime_control_plane(&self, session: &mut Session) -> io::Result<()> {
350 self.save_runtime_session(session).await
351 }
352
353 async fn load_runtime_control_plane(&self, session_id: &str) -> io::Result<Option<Session>> {
362 self.load_runtime_session(session_id).await
363 }
364
365 async fn update_task_list_control_plane(
377 &self,
378 session_id: &str,
379 task_list: &TaskList,
380 version: &str,
381 ) -> io::Result<bool> {
382 let Some(mut session) = self.load_runtime_session(session_id).await? else {
383 return Ok(false);
384 };
385 session.set_task_list(task_list.clone());
386 session.set_task_list_version_meta(version.to_string());
387 self.save_runtime_control_plane(&mut session).await?;
388 Ok(true)
389 }
390
391 async fn update_task_list_control_plane_if_version(
399 &self,
400 session_id: &str,
401 expected_version: &str,
402 expected_task_list: &TaskList,
403 task_list: &TaskList,
404 version: &str,
405 ) -> io::Result<bool> {
406 let _ = (
407 session_id,
408 expected_version,
409 expected_task_list,
410 task_list,
411 version,
412 );
413 Ok(false)
414 }
415
416 async fn update_task_list_control_planes_if_version(
425 &self,
426 session_id: &str,
427 shared_session_id: &str,
428 expected_version: &str,
429 expected_task_list: &TaskList,
430 task_list: &TaskList,
431 version: &str,
432 ) -> io::Result<bool> {
433 if session_id == shared_session_id {
434 return self
435 .update_task_list_control_plane_if_version(
436 session_id,
437 expected_version,
438 expected_task_list,
439 task_list,
440 version,
441 )
442 .await;
443 }
444 let _ = (
445 session_id,
446 shared_session_id,
447 expected_version,
448 expected_task_list,
449 task_list,
450 version,
451 );
452 Ok(false)
453 }
454
455 async fn checkpoint_runtime_session(&self, session: &mut Session) -> io::Result<()> {
465 if let Some(durable) = self.load_runtime_session(&session.id).await? {
466 session
467 .check_conversation_generation(&durable)
468 .map_err(|error| {
469 io::Error::new(
470 io::ErrorKind::WouldBlock,
471 crate::SessionAuthorityConflict(error.to_string()),
472 )
473 })?;
474 append_missing_runtime_messages(session, &durable);
475 merge_session_inbox_admission(session, &durable);
476 }
477 self.save_runtime_session(session).await
478 }
479
480 async fn checkpoint_live_bootstrap(
483 &self,
484 _session: &Session,
485 _inbox: Arc<dyn crate::SessionInboxPort>,
486 _gate: crate::AdmissionGate,
487 ) -> io::Result<crate::LiveBootstrapProof> {
488 Err(io::Error::new(
489 io::ErrorKind::Unsupported,
490 "live bootstrap checkpoint unsupported",
491 ))
492 }
493
494 async fn confirm_live_bootstrap(
495 &self,
496 _proof: &crate::LiveBootstrapProof,
497 _inbox: Arc<dyn crate::SessionInboxPort>,
498 _publish: crate::RootActorRuntimeEventPublisher,
499 ) -> io::Result<()> {
500 Err(io::Error::new(
501 io::ErrorKind::Unsupported,
502 "live bootstrap confirmation unsupported",
503 ))
504 }
505
506 async fn claim_scoped_loop_controls(
509 &self,
510 _session: &Session,
511 _inbox: Arc<dyn crate::SessionInboxPort>,
512 _active_run_id: &str,
513 _limit: usize,
514 ) -> io::Result<Vec<crate::ScopedLoopControlClaim>> {
515 Err(io::Error::new(
516 io::ErrorKind::Unsupported,
517 "scoped Loop claims unsupported",
518 ))
519 }
520
521 async fn checkpoint_scoped_loop_control(
524 &self,
525 _session: &mut Session,
526 _bindings: &[crate::ScopedLoopControlBinding],
527 _activation: Arc<dyn crate::SessionActivationPort>,
528 _input: Option<(
529 Arc<dyn crate::SessionInboxPort>,
530 crate::ScopedLoopControlClaim,
531 )>,
532 ) -> io::Result<()> {
533 Err(io::Error::new(
534 io::ErrorKind::Unsupported,
535 "scoped Loop checkpoint unsupported",
536 ))
537 }
538
539 async fn load_loop_control_checkpoint(&self, _session_id: &str) -> io::Result<Option<Session>> {
542 Err(io::Error::new(
543 io::ErrorKind::Unsupported,
544 "strict Loop checkpoint reload unsupported",
545 ))
546 }
547
548 async fn admit_root_inbox(
549 &self,
550 _session: &mut Session,
551 _inbox: Arc<dyn crate::SessionInboxPort>,
552 _active_run_id: Option<&str>,
553 ) -> io::Result<Option<RootInboxAdmission>> {
554 Ok(None)
555 }
556
557 async fn checkpoint_retrieval_window(
574 &self,
575 _expected_base: &Session,
576 _staged: &mut Session,
577 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
578 Err(io::Error::new(
579 io::ErrorKind::Unsupported,
580 "runtime persistence does not support retrieval-window checkpoints",
581 ))
582 }
583
584 async fn checkpoint_prompt_rewrite(
593 &self,
594 _expected_base: &Session,
595 _staged: &mut Session,
596 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
597 Err(io::Error::new(
598 io::ErrorKind::Unsupported,
599 "runtime persistence does not support prompt-rewrite checkpoints",
600 ))
601 }
602
603 async fn checkpoint_manual_archive_rejection(
611 &self,
612 _expected_base: &Session,
613 _staged: &mut Session,
614 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
615 Err(io::Error::new(
616 io::ErrorKind::Unsupported,
617 "runtime persistence does not support manual archive-rejection checkpoints",
618 ))
619 }
620
621 async fn checkpoint_manual_archive_consumption(
629 &self,
630 _expected_base: &Session,
631 _staged: &mut Session,
632 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
633 Err(io::Error::new(
634 io::ErrorKind::Unsupported,
635 "runtime persistence does not support manual archive-consumption checkpoints",
636 ))
637 }
638
639 async fn load_runtime_session(&self, _session_id: &str) -> io::Result<Option<Session>> {
644 Ok(None)
645 }
646
647 async fn load_runtime_admission_snapshot(
653 &self,
654 session_id: &str,
655 ) -> io::Result<Option<Session>> {
656 self.load_runtime_session(session_id).await
657 }
658
659 async fn clear_legacy_pending_messages(
663 &self,
664 session_id: &str,
665 expected: &[serde_json::Value],
666 ) -> io::Result<bool> {
667 let Some(mut latest) = self.load_runtime_session(session_id).await? else {
668 return Ok(false);
669 };
670 if latest.pending_injected_messages().as_deref() != Some(expected) {
671 return Ok(false);
672 }
673 latest.clear_pending_injected_messages();
674 self.save_runtime_session(&mut latest).await?;
675 Ok(true)
676 }
677
678 async fn append_token_usage_record(&self, session_id: &str, json_line: &str) -> io::Result<()> {
684 let _ = (session_id, json_line);
685 Ok(())
686 }
687}
688
689pub struct RootActorExecutionBinding {
693 pub persistence: Arc<dyn RuntimeSessionPersistence>,
694 pub directory: Arc<dyn crate::ActorDirectoryPort>,
695 pub owner: crate::RootActorRuntimeWrite,
696 pub lease_duration: std::time::Duration,
697 pub terminal_publisher: Option<crate::storage::RootActorRuntimePublisher>,
699 abandon: Option<Box<dyn FnOnce() + Send + Sync>>,
700}
701
702impl RootActorExecutionBinding {
703 pub fn new(
704 persistence: Arc<dyn RuntimeSessionPersistence>,
705 directory: Arc<dyn crate::ActorDirectoryPort>,
706 owner: crate::RootActorRuntimeWrite,
707 lease_duration: std::time::Duration,
708 abandon: impl FnOnce() + Send + Sync + 'static,
709 ) -> Self {
710 Self {
711 persistence,
712 directory,
713 owner,
714 lease_duration,
715 terminal_publisher: None,
716 abandon: Some(Box::new(abandon)),
717 }
718 }
719
720 pub fn with_terminal_publisher(
721 mut self,
722 publish: crate::storage::RootActorRuntimePublisher,
723 ) -> Self {
724 self.terminal_publisher = Some(publish);
725 self
726 }
727
728 pub fn disarm_abandonment(&mut self) {
731 self.abandon.take();
732 }
733}
734
735impl Drop for RootActorExecutionBinding {
736 fn drop(&mut self) {
737 if let Some(abandon) = self.abandon.take() {
738 abandon();
739 }
740 }
741}
742
743#[async_trait::async_trait]
744impl<T: RuntimeSessionPersistence + ?Sized> RuntimeSessionPersistence for Arc<T> {
745 fn inherited_child_wait(&self) -> Option<InheritedChildWait> {
746 (**self).inherited_child_wait()
747 }
748
749 fn bind_inherited_child_wait(
750 &self,
751 inherited: InheritedChildWait,
752 ) -> io::Result<Arc<dyn RuntimeSessionPersistence>> {
753 (**self).bind_inherited_child_wait(inherited)
754 }
755
756 fn root_actor_writer(&self) -> Option<crate::RootActorRuntimeWrite> {
757 (**self).root_actor_writer()
758 }
759
760 fn bind_root_inbox_receipt(
761 &self,
762 receipt: crate::SessionInboxReceipt,
763 ) -> io::Result<Arc<dyn RuntimeSessionPersistence>> {
764 (**self).bind_root_inbox_receipt(receipt)
765 }
766
767 fn root_inbox_receipt(&self) -> Option<&crate::SessionInboxReceipt> {
768 (**self).root_inbox_receipt()
769 }
770
771 fn root_actor_execution_required(&self, session: &Session) -> bool {
772 (**self).root_actor_execution_required(session)
773 }
774
775 async fn bind_root_actor_execution(
776 &self,
777 session: &Session,
778 run_id: &str,
779 ) -> io::Result<Option<RootActorExecutionBinding>> {
780 (**self).bind_root_actor_execution(session, run_id).await
781 }
782 async fn save_runtime_session(&self, session: &mut Session) -> io::Result<()> {
783 (**self).save_runtime_session(session).await
784 }
785
786 fn supports_bound_startup_session(&self) -> bool {
787 (**self).supports_bound_startup_session()
788 }
789
790 async fn save_bound_startup_session(
791 &self,
792 session: &mut Session,
793 expected: &Session,
794 ) -> io::Result<()> {
795 (**self).save_bound_startup_session(session, expected).await
796 }
797
798 async fn save_finalized_runtime_session(&self, session: &mut Session) -> io::Result<()> {
799 (**self).save_finalized_runtime_session(session).await
800 }
801
802 async fn save_finalized_runtime_with_inherited_child_wait(
803 &self,
804 session: &mut Session,
805 inherited: &crate::session::runtime_state::WaitingForChildrenState,
806 ) -> io::Result<()> {
807 (**self)
808 .save_finalized_runtime_with_inherited_child_wait(session, inherited)
809 .await
810 }
811
812 async fn seed_runtime_activation(&self, session: &mut Session) -> io::Result<()> {
813 (**self).seed_runtime_activation(session).await
814 }
815
816 async fn record_permission_posture_activation(
817 &self,
818 session_id: &str,
819 expected_audit_revision: Option<u64>,
820 seed: &PermissionAuditSeed,
821 ) -> io::Result<Option<Session>> {
822 (**self)
823 .record_permission_posture_activation(session_id, expected_audit_revision, seed)
824 .await
825 }
826
827 async fn save_runtime_control_plane(&self, session: &mut Session) -> io::Result<()> {
828 (**self).save_runtime_control_plane(session).await
829 }
830
831 async fn load_runtime_control_plane(&self, session_id: &str) -> io::Result<Option<Session>> {
832 (**self).load_runtime_control_plane(session_id).await
833 }
834
835 async fn update_task_list_control_plane(
836 &self,
837 session_id: &str,
838 task_list: &TaskList,
839 version: &str,
840 ) -> io::Result<bool> {
841 (**self)
842 .update_task_list_control_plane(session_id, task_list, version)
843 .await
844 }
845
846 async fn update_task_list_control_plane_if_version(
847 &self,
848 session_id: &str,
849 expected_version: &str,
850 expected_task_list: &TaskList,
851 task_list: &TaskList,
852 version: &str,
853 ) -> io::Result<bool> {
854 (**self)
855 .update_task_list_control_plane_if_version(
856 session_id,
857 expected_version,
858 expected_task_list,
859 task_list,
860 version,
861 )
862 .await
863 }
864
865 async fn update_task_list_control_planes_if_version(
866 &self,
867 session_id: &str,
868 shared_session_id: &str,
869 expected_version: &str,
870 expected_task_list: &TaskList,
871 task_list: &TaskList,
872 version: &str,
873 ) -> io::Result<bool> {
874 (**self)
875 .update_task_list_control_planes_if_version(
876 session_id,
877 shared_session_id,
878 expected_version,
879 expected_task_list,
880 task_list,
881 version,
882 )
883 .await
884 }
885
886 async fn checkpoint_runtime_session(&self, session: &mut Session) -> io::Result<()> {
887 (**self).checkpoint_runtime_session(session).await
888 }
889
890 async fn checkpoint_live_bootstrap(
891 &self,
892 session: &Session,
893 inbox: Arc<dyn crate::SessionInboxPort>,
894 gate: crate::AdmissionGate,
895 ) -> io::Result<crate::LiveBootstrapProof> {
896 (**self)
897 .checkpoint_live_bootstrap(session, inbox, gate)
898 .await
899 }
900
901 async fn confirm_live_bootstrap(
902 &self,
903 proof: &crate::LiveBootstrapProof,
904 inbox: Arc<dyn crate::SessionInboxPort>,
905 publish: crate::RootActorRuntimeEventPublisher,
906 ) -> io::Result<()> {
907 (**self).confirm_live_bootstrap(proof, inbox, publish).await
908 }
909
910 async fn claim_scoped_loop_controls(
911 &self,
912 session: &Session,
913 inbox: Arc<dyn crate::SessionInboxPort>,
914 active_run_id: &str,
915 limit: usize,
916 ) -> io::Result<Vec<crate::ScopedLoopControlClaim>> {
917 (**self)
918 .claim_scoped_loop_controls(session, inbox, active_run_id, limit)
919 .await
920 }
921
922 async fn checkpoint_scoped_loop_control(
923 &self,
924 session: &mut Session,
925 bindings: &[crate::ScopedLoopControlBinding],
926 activation: Arc<dyn crate::SessionActivationPort>,
927 input: Option<(
928 Arc<dyn crate::SessionInboxPort>,
929 crate::ScopedLoopControlClaim,
930 )>,
931 ) -> io::Result<()> {
932 (**self)
933 .checkpoint_scoped_loop_control(session, bindings, activation, input)
934 .await
935 }
936
937 async fn load_loop_control_checkpoint(&self, session_id: &str) -> io::Result<Option<Session>> {
938 (**self).load_loop_control_checkpoint(session_id).await
939 }
940
941 async fn admit_root_inbox(
942 &self,
943 session: &mut Session,
944 inbox: Arc<dyn crate::SessionInboxPort>,
945 active_run_id: Option<&str>,
946 ) -> io::Result<Option<RootInboxAdmission>> {
947 (**self)
948 .admit_root_inbox(session, inbox, active_run_id)
949 .await
950 }
951
952 async fn checkpoint_retrieval_window(
953 &self,
954 expected_base: &Session,
955 staged: &mut Session,
956 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
957 (**self)
958 .checkpoint_retrieval_window(expected_base, staged)
959 .await
960 }
961
962 async fn checkpoint_prompt_rewrite(
963 &self,
964 expected_base: &Session,
965 staged: &mut Session,
966 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
967 (**self)
968 .checkpoint_prompt_rewrite(expected_base, staged)
969 .await
970 }
971
972 async fn checkpoint_manual_archive_rejection(
973 &self,
974 expected_base: &Session,
975 staged: &mut Session,
976 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
977 (**self)
978 .checkpoint_manual_archive_rejection(expected_base, staged)
979 .await
980 }
981
982 async fn checkpoint_manual_archive_consumption(
983 &self,
984 expected_base: &Session,
985 staged: &mut Session,
986 ) -> io::Result<RetrievalWindowCheckpointOutcome> {
987 (**self)
988 .checkpoint_manual_archive_consumption(expected_base, staged)
989 .await
990 }
991
992 async fn load_runtime_session(&self, session_id: &str) -> io::Result<Option<Session>> {
993 (**self).load_runtime_session(session_id).await
994 }
995
996 async fn load_runtime_admission_snapshot(
997 &self,
998 session_id: &str,
999 ) -> io::Result<Option<Session>> {
1000 (**self).load_runtime_admission_snapshot(session_id).await
1001 }
1002
1003 async fn clear_legacy_pending_messages(
1004 &self,
1005 session_id: &str,
1006 expected: &[serde_json::Value],
1007 ) -> io::Result<bool> {
1008 (**self)
1009 .clear_legacy_pending_messages(session_id, expected)
1010 .await
1011 }
1012
1013 async fn append_token_usage_record(&self, session_id: &str, json_line: &str) -> io::Result<()> {
1014 (**self)
1015 .append_token_usage_record(session_id, json_line)
1016 .await
1017 }
1018}