1use async_trait::async_trait;
4use starweaver_context::{AgentCheckpoint, ResumableState};
5use starweaver_core::{RunId, SessionId};
6use starweaver_stream::{AgentStreamRecord, ReplayEvent};
7
8use crate::{
9 AcquireBackgroundSubagentContinuation, AcquireRunAdmission, AdmitRunControl,
10 BackgroundSubagentContinuationReceipt, BackgroundSubagentRecord,
11 DurableBackgroundSubagentDeliveryClaim, DurableBackgroundSubagentDeliveryRelease,
12 DurableControlReceipt, DurableRunControlIntent, DurableRunControlStatus, RunAdmissionLease,
13 RunAdmissionReceipt, SessionContinuationFence, UpdateManagedSession,
14 approval::{ApprovalRecord, DeferredToolRecord},
15 claim::HitlResumeClaim,
16 error::{SessionStoreError, SessionStoreResult},
17 evidence::RunEvidenceCommit,
18 host_events::{
19 DurableHostEventClass, DurableHostEventPage, DurableHostEventQuery, DurableHostEventRecord,
20 DurableHostEventScope, PendingHostEventPublication,
21 },
22 publication::{PendingStreamPublication, StreamPublicationTarget},
23 records::{
24 EnvironmentStateRef, RunRecord, RunStatus, RunTerminalProjection, SessionRecord,
25 SessionStatus, StreamCursorRef,
26 },
27 resume::SessionResumeSnapshot,
28 trace::{CompactRunTrace, CompactSessionTrace},
29};
30
31fn management_unsupported<T>() -> SessionStoreResult<T> {
32 Err(SessionStoreError::Failed(
33 "session store does not support agent session management".to_string(),
34 ))
35}
36
37#[derive(Clone, Debug, Default, Eq, PartialEq)]
39pub struct SessionFilter {
40 pub status: Option<SessionStatus>,
42 pub profile: Option<String>,
44 pub workspace: Option<String>,
46 pub limit: Option<usize>,
48}
49
50pub const MAX_STABLE_PAGE_SIZE: usize = 200;
52
53#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct SessionPageKey {
56 pub updated_at: chrono::DateTime<chrono::Utc>,
58 pub session_id: SessionId,
60}
61
62impl SessionPageKey {
63 #[must_use]
65 pub fn from_session(session: &SessionRecord) -> Self {
66 Self {
67 updated_at: session.updated_at,
68 session_id: session.session_id.clone(),
69 }
70 }
71}
72
73#[derive(Clone, Debug, Eq, PartialEq)]
75pub struct SessionPageQuery {
76 after: Option<SessionPageKey>,
77 limit: usize,
78}
79
80impl SessionPageQuery {
81 pub fn new(after: Option<SessionPageKey>, limit: usize) -> SessionStoreResult<Self> {
87 validate_page_limit(limit)?;
88 Ok(Self { after, limit })
89 }
90
91 #[must_use]
93 pub const fn after(&self) -> Option<&SessionPageKey> {
94 self.after.as_ref()
95 }
96
97 #[must_use]
99 pub const fn limit(&self) -> usize {
100 self.limit
101 }
102}
103
104#[derive(Clone, Debug, Eq, PartialEq)]
106pub struct SessionPage {
107 pub sessions: Vec<SessionRecord>,
109 pub next_key: Option<SessionPageKey>,
111 pub has_more: bool,
113}
114
115#[derive(Clone, Debug, Eq, PartialEq)]
117pub struct RunPageKey {
118 pub sequence_no: usize,
120}
121
122impl RunPageKey {
123 #[must_use]
125 pub const fn from_run(run: &RunRecord) -> Self {
126 Self {
127 sequence_no: run.sequence_no,
128 }
129 }
130}
131
132#[derive(Clone, Debug, Eq, PartialEq)]
134pub struct RunPageQuery {
135 before: Option<RunPageKey>,
136 limit: usize,
137}
138
139impl RunPageQuery {
140 pub fn new(before: Option<RunPageKey>, limit: usize) -> SessionStoreResult<Self> {
146 validate_page_limit(limit)?;
147 Ok(Self { before, limit })
148 }
149
150 #[must_use]
152 pub const fn before(&self) -> Option<&RunPageKey> {
153 self.before.as_ref()
154 }
155
156 #[must_use]
158 pub const fn limit(&self) -> usize {
159 self.limit
160 }
161}
162
163#[derive(Clone, Debug, Eq, PartialEq)]
165pub struct RunPage {
166 pub runs: Vec<RunRecord>,
168 pub next_key: Option<RunPageKey>,
170 pub has_more: bool,
172}
173
174#[derive(Clone, Debug, Eq, PartialEq)]
176pub struct InteractionPageKey {
177 pub updated_at: chrono::DateTime<chrono::Utc>,
179 pub interaction_id: String,
181}
182
183#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
185pub enum InteractionStateFilter {
186 #[default]
188 All,
189 Unresolved,
191 Resolved,
193}
194
195#[derive(Clone, Debug, Eq, PartialEq)]
197pub struct InteractionPageQuery {
198 session_id: Option<SessionId>,
199 run_id: Option<RunId>,
200 after: Option<InteractionPageKey>,
201 limit: usize,
202 state: InteractionStateFilter,
203}
204
205impl InteractionPageQuery {
206 pub fn new(
212 session_id: Option<SessionId>,
213 run_id: Option<RunId>,
214 after: Option<InteractionPageKey>,
215 limit: usize,
216 ) -> SessionStoreResult<Self> {
217 validate_page_limit(limit)?;
218 Ok(Self {
219 session_id,
220 run_id,
221 after,
222 limit,
223 state: InteractionStateFilter::All,
224 })
225 }
226
227 #[must_use]
229 pub const fn with_state(mut self, state: InteractionStateFilter) -> Self {
230 self.state = state;
231 self
232 }
233
234 #[must_use]
236 pub const fn session_id(&self) -> Option<&SessionId> {
237 self.session_id.as_ref()
238 }
239
240 #[must_use]
242 pub const fn run_id(&self) -> Option<&RunId> {
243 self.run_id.as_ref()
244 }
245
246 #[must_use]
248 pub const fn after(&self) -> Option<&InteractionPageKey> {
249 self.after.as_ref()
250 }
251
252 #[must_use]
254 pub const fn limit(&self) -> usize {
255 self.limit
256 }
257
258 #[must_use]
260 pub const fn state(&self) -> InteractionStateFilter {
261 self.state
262 }
263}
264
265#[derive(Clone, Debug, Eq, PartialEq)]
267pub struct InteractionPage<T> {
268 pub records: Vec<T>,
270 pub next_key: Option<InteractionPageKey>,
272 pub has_more: bool,
274}
275
276fn validate_page_limit(limit: usize) -> SessionStoreResult<()> {
277 if !(1..=MAX_STABLE_PAGE_SIZE).contains(&limit) {
278 return Err(SessionStoreError::Failed(format!(
279 "stable page limit must be between 1 and {MAX_STABLE_PAGE_SIZE}"
280 )));
281 }
282 Ok(())
283}
284
285#[async_trait]
287pub trait SessionStore: Send + Sync {
288 async fn commit_run_evidence(&self, commit: RunEvidenceCommit)
295 -> SessionStoreResult<RunRecord>;
296
297 async fn commit_run_evidence_fenced(
303 &self,
304 _lease: &RunAdmissionLease,
305 _commit: RunEvidenceCommit,
306 ) -> SessionStoreResult<RunRecord> {
307 management_unsupported()
308 }
309
310 async fn append_replay_events_fenced(
317 &self,
318 _lease: &RunAdmissionLease,
319 _events: Vec<ReplayEvent>,
320 ) -> SessionStoreResult<()> {
321 management_unsupported()
322 }
323
324 async fn commit_checkpoint(
330 &self,
331 session_id: &SessionId,
332 checkpoint: AgentCheckpoint,
333 ) -> SessionStoreResult<()>;
334
335 async fn commit_checkpoint_fenced(
339 &self,
340 _lease: &RunAdmissionLease,
341 _checkpoint: AgentCheckpoint,
342 ) -> SessionStoreResult<()> {
343 management_unsupported()
344 }
345
346 async fn claim_hitl_resume(&self, _claim: HitlResumeClaim) -> SessionStoreResult<()> {
351 Err(SessionStoreError::Failed(
352 "session store does not support exclusive HITL resume claims".to_string(),
353 ))
354 }
355
356 async fn start_hitl_resume_effect(
361 &self,
362 _lease: &RunAdmissionLease,
363 _source_run_id: &RunId,
364 _claim_id: &str,
365 ) -> SessionStoreResult<()> {
366 management_unsupported()
367 }
368
369 async fn abort_admitted_hitl_resume(
375 &self,
376 _lease: &RunAdmissionLease,
377 _source_run_id: &RunId,
378 _claim_id: &str,
379 _output_preview: &str,
380 ) -> SessionStoreResult<crate::HitlResumeAbortOutcome> {
381 management_unsupported()
382 }
383
384 async fn mark_hitl_resume_started(
386 &self,
387 _session_id: &SessionId,
388 _run_id: &RunId,
389 _claim_id: &str,
390 ) -> SessionStoreResult<()> {
391 Err(SessionStoreError::Failed(
392 "session store does not support exclusive HITL resume claims".to_string(),
393 ))
394 }
395
396 async fn release_hitl_resume_claim(
398 &self,
399 _session_id: &SessionId,
400 _run_id: &RunId,
401 _claim_id: &str,
402 ) -> SessionStoreResult<()> {
403 Err(SessionStoreError::Failed(
404 "session store does not support exclusive HITL resume claims".to_string(),
405 ))
406 }
407
408 async fn pending_stream_publications(
410 &self,
411 _session_id: &SessionId,
412 ) -> SessionStoreResult<Vec<PendingStreamPublication>> {
413 Err(SessionStoreError::Failed(
414 "session store does not support transactional stream publication".to_string(),
415 ))
416 }
417
418 async fn acknowledge_stream_publication(
420 &self,
421 _publication_id: &str,
422 _target: StreamPublicationTarget,
423 ) -> SessionStoreResult<()> {
424 Err(SessionStoreError::Failed(
425 "session store does not support transactional stream publication".to_string(),
426 ))
427 }
428
429 async fn enqueue_host_event_publications(
434 &self,
435 _publications: Vec<PendingHostEventPublication>,
436 ) -> SessionStoreResult<()> {
437 management_unsupported()
438 }
439
440 async fn pending_host_event_publications(
442 &self,
443 _limit: usize,
444 ) -> SessionStoreResult<Vec<PendingHostEventPublication>> {
445 management_unsupported()
446 }
447
448 async fn materialize_host_event_publications(
453 &self,
454 _limit: usize,
455 ) -> SessionStoreResult<Vec<DurableHostEventRecord>> {
456 management_unsupported()
457 }
458
459 async fn replay_host_events(
461 &self,
462 _query: DurableHostEventQuery,
463 ) -> SessionStoreResult<DurableHostEventPage> {
464 management_unsupported()
465 }
466
467 async fn host_event_fence(
469 &self,
470 _scope: &DurableHostEventScope,
471 _event_classes: &[DurableHostEventClass],
472 ) -> SessionStoreResult<Option<u64>> {
473 management_unsupported()
474 }
475
476 async fn create_session_idempotent(
478 &self,
479 _session: SessionRecord,
480 _idempotency_key: &str,
481 _command_fingerprint: &str,
482 ) -> SessionStoreResult<SessionRecord> {
483 management_unsupported()
484 }
485
486 async fn create_session_idempotent_with_host_events(
491 &self,
492 _session: SessionRecord,
493 _idempotency_key: &str,
494 _command_fingerprint: &str,
495 _publications: Vec<PendingHostEventPublication>,
496 ) -> SessionStoreResult<SessionRecord> {
497 management_unsupported()
498 }
499
500 async fn load_session_mutation_receipt(
502 &self,
503 _namespace_id: &str,
504 _idempotency_key: &str,
505 _command_fingerprint: &str,
506 ) -> SessionStoreResult<Option<SessionRecord>> {
507 management_unsupported()
508 }
509
510 async fn update_managed_session(
512 &self,
513 _command: UpdateManagedSession,
514 _command_fingerprint: &str,
515 ) -> SessionStoreResult<SessionRecord> {
516 management_unsupported()
517 }
518
519 async fn update_managed_session_with_host_events(
521 &self,
522 _command: UpdateManagedSession,
523 _command_fingerprint: &str,
524 _publications: Vec<PendingHostEventPublication>,
525 ) -> SessionStoreResult<SessionRecord> {
526 management_unsupported()
527 }
528
529 async fn acquire_session_deletion_fence(
531 &self,
532 _session_id: &SessionId,
533 _expected_revision: u64,
534 _fence_id: &str,
535 _requested_by: &str,
536 _idempotency_key: &str,
537 _command_fingerprint: &str,
538 ) -> SessionStoreResult<SessionRecord> {
539 management_unsupported()
540 }
541
542 async fn tombstone_session(
544 &self,
545 _session_id: &SessionId,
546 _fence_id: &str,
547 ) -> SessionStoreResult<SessionRecord> {
548 management_unsupported()
549 }
550
551 #[allow(clippy::too_many_arguments)]
556 async fn tombstone_session_idempotent_with_host_events(
557 &self,
558 _session_id: &SessionId,
559 _fence_id: &str,
560 _idempotency_key: &str,
561 _command_fingerprint: &str,
562 _publications: Vec<PendingHostEventPublication>,
563 ) -> SessionStoreResult<SessionRecord> {
564 management_unsupported()
565 }
566
567 async fn session_continuation_fence(
569 &self,
570 _namespace_id: &str,
571 _session_id: &SessionId,
572 ) -> SessionStoreResult<SessionContinuationFence> {
573 management_unsupported()
574 }
575
576 async fn acquire_run_admission(
578 &self,
579 _request: AcquireRunAdmission,
580 ) -> SessionStoreResult<RunAdmissionReceipt> {
581 management_unsupported()
582 }
583
584 async fn load_run_admission_receipt(
586 &self,
587 _namespace_id: &str,
588 _idempotency_key: &str,
589 _command_fingerprint: &str,
590 ) -> SessionStoreResult<Option<RunAdmissionReceipt>> {
591 management_unsupported()
592 }
593
594 async fn heartbeat_run_admission(
596 &self,
597 _lease: &RunAdmissionLease,
598 _lease_expires_at: chrono::DateTime<chrono::Utc>,
599 ) -> SessionStoreResult<RunAdmissionLease> {
600 management_unsupported()
601 }
602
603 async fn release_run_admission(&self, _lease: &RunAdmissionLease) -> SessionStoreResult<()> {
605 management_unsupported()
606 }
607
608 async fn update_run_status_fenced(
613 &self,
614 _lease: &RunAdmissionLease,
615 _status: RunStatus,
616 _output_preview: Option<String>,
617 ) -> SessionStoreResult<RunRecord> {
618 management_unsupported()
619 }
620
621 async fn finalize_run_admission(
628 &self,
629 _lease: &RunAdmissionLease,
630 _terminal: RunTerminalProjection,
631 ) -> SessionStoreResult<RunRecord> {
632 management_unsupported()
633 }
634
635 async fn load_run_admission(
637 &self,
638 _target: &crate::ManagedRunTarget,
639 ) -> SessionStoreResult<Option<RunAdmissionLease>> {
640 management_unsupported()
641 }
642
643 async fn reconcile_expired_run_admissions(
651 &self,
652 _namespace_id: &str,
653 _now: chrono::DateTime<chrono::Utc>,
654 ) -> SessionStoreResult<Vec<crate::ManagedRunTarget>> {
655 management_unsupported()
656 }
657
658 async fn admit_run_control(
664 &self,
665 _request: AdmitRunControl,
666 ) -> SessionStoreResult<DurableRunControlIntent> {
667 management_unsupported()
668 }
669
670 async fn load_run_control_intent(
672 &self,
673 _target: &crate::ManagedRunTarget,
674 _operation_id: &str,
675 ) -> SessionStoreResult<Option<DurableRunControlIntent>> {
676 management_unsupported()
677 }
678
679 async fn list_run_control_intents(
681 &self,
682 _target: &crate::ManagedRunTarget,
683 _statuses: &[DurableRunControlStatus],
684 _limit: usize,
685 ) -> SessionStoreResult<Vec<DurableRunControlIntent>> {
686 management_unsupported()
687 }
688
689 async fn advance_run_control_intent(
694 &self,
695 _lease: &RunAdmissionLease,
696 _operation_id: &str,
697 _expected: DurableRunControlStatus,
698 _next: DurableRunControlStatus,
699 _occurred_at: chrono::DateTime<chrono::Utc>,
700 ) -> SessionStoreResult<DurableRunControlIntent> {
701 management_unsupported()
702 }
703
704 async fn reconcile_run_control_intent(
706 &self,
707 _target: &crate::ManagedRunTarget,
708 _operation_id: &str,
709 _occurred_at: chrono::DateTime<chrono::Utc>,
710 ) -> SessionStoreResult<DurableRunControlIntent> {
711 management_unsupported()
712 }
713
714 async fn load_control_receipt(
716 &self,
717 _target: &crate::ManagedRunTarget,
718 _idempotency_key: &str,
719 ) -> SessionStoreResult<Option<DurableControlReceipt>> {
720 management_unsupported()
721 }
722
723 async fn reserve_control_receipt(
725 &self,
726 _receipt: DurableControlReceipt,
727 ) -> SessionStoreResult<DurableControlReceipt> {
728 management_unsupported()
729 }
730
731 async fn update_control_receipt_state(
733 &self,
734 _receipt_id: &str,
735 _state: &str,
736 ) -> SessionStoreResult<DurableControlReceipt> {
737 management_unsupported()
738 }
739
740 async fn drain_background_subagent_operations(&self) -> SessionStoreResult<()> {
746 management_unsupported()
747 }
748
749 async fn record_background_subagent_acceptance(
751 &self,
752 _record: BackgroundSubagentRecord,
753 ) -> SessionStoreResult<BackgroundSubagentRecord> {
754 management_unsupported()
755 }
756
757 async fn update_background_subagent_execution(
759 &self,
760 _record: BackgroundSubagentRecord,
761 ) -> SessionStoreResult<BackgroundSubagentRecord> {
762 management_unsupported()
763 }
764
765 async fn heartbeat_background_subagent(
767 &self,
768 _attempt_id: &starweaver_core::SubagentAttemptId,
769 _host_instance_id: &str,
770 _fencing_generation: u64,
771 _lease_expires_at: chrono::DateTime<chrono::Utc>,
772 ) -> SessionStoreResult<BackgroundSubagentRecord> {
773 management_unsupported()
774 }
775
776 async fn commit_background_subagent_terminal(
778 &self,
779 commit: crate::BackgroundSubagentTerminalCommit,
780 ) -> SessionStoreResult<BackgroundSubagentRecord> {
781 if commit.artifact.is_some() {
782 return management_unsupported();
783 }
784 self.record_background_subagent_terminal(commit.record)
785 .await
786 }
787
788 async fn load_background_subagent_artifact(
790 &self,
791 _artifact_ref: &str,
792 ) -> SessionStoreResult<crate::BackgroundSubagentArtifact> {
793 management_unsupported()
794 }
795
796 async fn expire_background_subagent_retention(
798 &self,
799 _namespace_id: &str,
800 _now: chrono::DateTime<chrono::Utc>,
801 _limit: usize,
802 ) -> SessionStoreResult<Vec<BackgroundSubagentRecord>> {
803 management_unsupported()
804 }
805
806 async fn record_background_subagent_terminal(
808 &self,
809 _record: BackgroundSubagentRecord,
810 ) -> SessionStoreResult<BackgroundSubagentRecord> {
811 management_unsupported()
812 }
813
814 async fn load_background_subagent(
816 &self,
817 _attempt_id: &starweaver_core::SubagentAttemptId,
818 ) -> SessionStoreResult<BackgroundSubagentRecord> {
819 management_unsupported()
820 }
821
822 async fn list_background_subagents(
824 &self,
825 _namespace_id: &str,
826 _session_id: Option<&SessionId>,
827 _limit: usize,
828 ) -> SessionStoreResult<Vec<BackgroundSubagentRecord>> {
829 management_unsupported()
830 }
831
832 async fn list_pending_background_subagents(
834 &self,
835 _namespace_id: &str,
836 _session_id: Option<&SessionId>,
837 _limit: usize,
838 ) -> SessionStoreResult<Vec<BackgroundSubagentRecord>> {
839 management_unsupported()
840 }
841
842 async fn claim_background_subagent_delivery(
844 &self,
845 _attempt_id: &starweaver_core::SubagentAttemptId,
846 _claim: DurableBackgroundSubagentDeliveryClaim,
847 ) -> SessionStoreResult<BackgroundSubagentRecord> {
848 management_unsupported()
849 }
850
851 async fn acknowledge_background_subagent_delivery(
853 &self,
854 _attempt_id: &starweaver_core::SubagentAttemptId,
855 _claim_id: &str,
856 ) -> SessionStoreResult<BackgroundSubagentRecord> {
857 management_unsupported()
858 }
859
860 async fn release_background_subagent_delivery(
862 &self,
863 _attempt_id: &starweaver_core::SubagentAttemptId,
864 _claim_id: &str,
865 _release: DurableBackgroundSubagentDeliveryRelease,
866 ) -> SessionStoreResult<BackgroundSubagentRecord> {
867 management_unsupported()
868 }
869
870 async fn acquire_background_subagent_continuation(
872 &self,
873 _request: AcquireBackgroundSubagentContinuation,
874 ) -> SessionStoreResult<BackgroundSubagentContinuationReceipt> {
875 management_unsupported()
876 }
877
878 async fn reconcile_background_subagents(
880 &self,
881 _namespace_id: &str,
882 _now: chrono::DateTime<chrono::Utc>,
883 ) -> SessionStoreResult<Vec<BackgroundSubagentRecord>> {
884 management_unsupported()
885 }
886
887 async fn save_session(&self, session: SessionRecord) -> SessionStoreResult<()>;
892
893 async fn load_session(&self, session_id: &SessionId) -> SessionStoreResult<SessionRecord>;
895
896 async fn list_sessions(&self, filter: SessionFilter) -> SessionStoreResult<Vec<SessionRecord>>;
898
899 async fn list_session_page(&self, query: SessionPageQuery) -> SessionStoreResult<SessionPage>;
901
902 async fn update_session_status(
904 &self,
905 session_id: &SessionId,
906 status: SessionStatus,
907 ) -> SessionStoreResult<()>;
908
909 async fn save_context_state(
911 &self,
912 session_id: &SessionId,
913 state: ResumableState,
914 ) -> SessionStoreResult<()>;
915
916 async fn save_environment_state(
918 &self,
919 session_id: &SessionId,
920 environment_state: EnvironmentStateRef,
921 ) -> SessionStoreResult<()>;
922
923 async fn append_run(&self, run: RunRecord) -> SessionStoreResult<()>;
928
929 async fn load_run(
931 &self,
932 session_id: &SessionId,
933 run_id: &RunId,
934 ) -> SessionStoreResult<RunRecord>;
935
936 async fn list_runs(&self, session_id: &SessionId) -> SessionStoreResult<Vec<RunRecord>>;
938
939 async fn list_recent_runs(
944 &self,
945 session_id: &SessionId,
946 limit: usize,
947 ) -> SessionStoreResult<Vec<RunRecord>>;
948
949 async fn list_run_page(
951 &self,
952 session_id: &SessionId,
953 query: RunPageQuery,
954 ) -> SessionStoreResult<RunPage>;
955
956 async fn update_run_status(
961 &self,
962 session_id: &SessionId,
963 run_id: &RunId,
964 status: RunStatus,
965 output_preview: Option<String>,
966 ) -> SessionStoreResult<()>;
967
968 async fn append_checkpoint(
970 &self,
971 session_id: &SessionId,
972 checkpoint: AgentCheckpoint,
973 ) -> SessionStoreResult<()>;
974
975 async fn load_checkpoints(
977 &self,
978 session_id: &SessionId,
979 run_id: &RunId,
980 ) -> SessionStoreResult<Vec<AgentCheckpoint>>;
981
982 async fn latest_checkpoint(
984 &self,
985 session_id: &SessionId,
986 run_id: &RunId,
987 ) -> SessionStoreResult<Option<AgentCheckpoint>> {
988 let checkpoints = self.load_checkpoints(session_id, run_id).await?;
989 Ok(checkpoints.into_iter().last())
990 }
991
992 async fn append_stream_records(
994 &self,
995 session_id: &SessionId,
996 run_id: &RunId,
997 records: Vec<AgentStreamRecord>,
998 ) -> SessionStoreResult<()>;
999
1000 async fn replay_stream_records(
1002 &self,
1003 session_id: &SessionId,
1004 run_id: &RunId,
1005 ) -> SessionStoreResult<Vec<AgentStreamRecord>>;
1006
1007 async fn replay_stream_records_after(
1009 &self,
1010 session_id: &SessionId,
1011 run_id: &RunId,
1012 after_sequence: Option<usize>,
1013 ) -> SessionStoreResult<Vec<AgentStreamRecord>> {
1014 let records = self.replay_stream_records(session_id, run_id).await?;
1015 Ok(records
1016 .into_iter()
1017 .filter(|record| after_sequence.is_none_or(|cursor| record.sequence > cursor))
1018 .collect())
1019 }
1020
1021 async fn save_stream_cursor(
1023 &self,
1024 session_id: &SessionId,
1025 run_id: &RunId,
1026 cursor: StreamCursorRef,
1027 ) -> SessionStoreResult<()>;
1028
1029 async fn append_approval(&self, approval: ApprovalRecord) -> SessionStoreResult<()>;
1031
1032 async fn load_approvals(
1034 &self,
1035 session_id: &SessionId,
1036 run_id: &RunId,
1037 ) -> SessionStoreResult<Vec<ApprovalRecord>>;
1038
1039 async fn append_deferred_tool(&self, record: DeferredToolRecord) -> SessionStoreResult<()>;
1041
1042 async fn load_deferred_tools(
1044 &self,
1045 session_id: &SessionId,
1046 run_id: &RunId,
1047 ) -> SessionStoreResult<Vec<DeferredToolRecord>>;
1048
1049 async fn resume_snapshot(
1051 &self,
1052 _session_id: &SessionId,
1053 _run_id: &RunId,
1054 ) -> SessionStoreResult<SessionResumeSnapshot> {
1055 Err(SessionStoreError::Failed(
1056 "session store does not support per-run resume snapshots".to_string(),
1057 ))
1058 }
1059
1060 async fn prepare_continuation(
1065 &self,
1066 session_id: &SessionId,
1067 run_id: &RunId,
1068 mode: crate::ContinuationPreparationMode,
1069 ) -> SessionStoreResult<crate::PreparedContinuation> {
1070 let snapshot = self.resume_snapshot(session_id, run_id).await?;
1071 match mode {
1072 crate::ContinuationPreparationMode::Ordinary => {
1073 crate::PreparedContinuation::ordinary(snapshot)
1074 }
1075 crate::ContinuationPreparationMode::WaitingHitl => {
1076 crate::PreparedContinuation::waiting_hitl(snapshot)
1077 }
1078 }
1079 .map_err(|error| SessionStoreError::Conflict(error.to_string()))
1080 }
1081
1082 async fn compact_run_trace(
1084 &self,
1085 session_id: &SessionId,
1086 run_id: &RunId,
1087 ) -> SessionStoreResult<CompactRunTrace>;
1088
1089 async fn compact_session_trace(
1091 &self,
1092 session_id: &SessionId,
1093 ) -> SessionStoreResult<CompactSessionTrace>;
1094}