1use async_trait::async_trait;
4use starweaver_context::{AgentCheckpoint, ResumableState};
5use starweaver_core::{RunId, SessionId};
6use starweaver_stream::AgentStreamRecord;
7
8use crate::{
9 AcquireBackgroundSubagentContinuation, AcquireRunAdmission,
10 BackgroundSubagentContinuationReceipt, BackgroundSubagentRecord,
11 DurableBackgroundSubagentDeliveryClaim, DurableBackgroundSubagentDeliveryRelease,
12 DurableControlReceipt, RunAdmissionLease, RunAdmissionReceipt, SessionContinuationFence,
13 UpdateManagedSession,
14 approval::{ApprovalRecord, DeferredToolRecord},
15 claim::HitlResumeClaim,
16 error::{SessionStoreError, SessionStoreResult},
17 evidence::RunEvidenceCommit,
18 publication::{PendingStreamPublication, StreamPublicationTarget},
19 records::{
20 EnvironmentStateRef, RunRecord, RunStatus, SessionRecord, SessionStatus, StreamCursorRef,
21 },
22 resume::SessionResumeSnapshot,
23 trace::{CompactRunTrace, CompactSessionTrace},
24};
25
26fn management_unsupported<T>() -> SessionStoreResult<T> {
27 Err(SessionStoreError::Failed(
28 "session store does not support agent session management".to_string(),
29 ))
30}
31
32#[derive(Clone, Debug, Default, Eq, PartialEq)]
34pub struct SessionFilter {
35 pub status: Option<SessionStatus>,
37 pub profile: Option<String>,
39 pub workspace: Option<String>,
41 pub limit: Option<usize>,
43}
44
45#[async_trait]
47pub trait SessionStore: Send + Sync {
48 async fn commit_run_evidence(&self, commit: RunEvidenceCommit)
53 -> SessionStoreResult<RunRecord>;
54
55 async fn commit_checkpoint(
61 &self,
62 session_id: &SessionId,
63 checkpoint: AgentCheckpoint,
64 ) -> SessionStoreResult<()>;
65
66 async fn claim_hitl_resume(&self, _claim: HitlResumeClaim) -> SessionStoreResult<()> {
71 Err(SessionStoreError::Failed(
72 "session store does not support exclusive HITL resume claims".to_string(),
73 ))
74 }
75
76 async fn mark_hitl_resume_started(
78 &self,
79 _session_id: &SessionId,
80 _run_id: &RunId,
81 _claim_id: &str,
82 ) -> SessionStoreResult<()> {
83 Err(SessionStoreError::Failed(
84 "session store does not support exclusive HITL resume claims".to_string(),
85 ))
86 }
87
88 async fn release_hitl_resume_claim(
90 &self,
91 _session_id: &SessionId,
92 _run_id: &RunId,
93 _claim_id: &str,
94 ) -> SessionStoreResult<()> {
95 Err(SessionStoreError::Failed(
96 "session store does not support exclusive HITL resume claims".to_string(),
97 ))
98 }
99
100 async fn pending_stream_publications(
102 &self,
103 _session_id: &SessionId,
104 ) -> SessionStoreResult<Vec<PendingStreamPublication>> {
105 Err(SessionStoreError::Failed(
106 "session store does not support transactional stream publication".to_string(),
107 ))
108 }
109
110 async fn acknowledge_stream_publication(
112 &self,
113 _publication_id: &str,
114 _target: StreamPublicationTarget,
115 ) -> SessionStoreResult<()> {
116 Err(SessionStoreError::Failed(
117 "session store does not support transactional stream publication".to_string(),
118 ))
119 }
120
121 async fn create_session_idempotent(
123 &self,
124 _session: SessionRecord,
125 _idempotency_key: &str,
126 _command_fingerprint: &str,
127 ) -> SessionStoreResult<SessionRecord> {
128 management_unsupported()
129 }
130
131 async fn update_managed_session(
133 &self,
134 _command: UpdateManagedSession,
135 _command_fingerprint: &str,
136 ) -> SessionStoreResult<SessionRecord> {
137 management_unsupported()
138 }
139
140 async fn acquire_session_deletion_fence(
142 &self,
143 _session_id: &SessionId,
144 _expected_revision: u64,
145 _fence_id: &str,
146 _requested_by: &str,
147 _idempotency_key: &str,
148 _command_fingerprint: &str,
149 ) -> SessionStoreResult<SessionRecord> {
150 management_unsupported()
151 }
152
153 async fn tombstone_session(
155 &self,
156 _session_id: &SessionId,
157 _fence_id: &str,
158 ) -> SessionStoreResult<SessionRecord> {
159 management_unsupported()
160 }
161
162 async fn session_continuation_fence(
164 &self,
165 _namespace_id: &str,
166 _session_id: &SessionId,
167 ) -> SessionStoreResult<SessionContinuationFence> {
168 management_unsupported()
169 }
170
171 async fn acquire_run_admission(
173 &self,
174 _request: AcquireRunAdmission,
175 ) -> SessionStoreResult<RunAdmissionReceipt> {
176 management_unsupported()
177 }
178
179 async fn heartbeat_run_admission(
181 &self,
182 _lease: &RunAdmissionLease,
183 _lease_expires_at: chrono::DateTime<chrono::Utc>,
184 ) -> SessionStoreResult<RunAdmissionLease> {
185 management_unsupported()
186 }
187
188 async fn release_run_admission(&self, _lease: &RunAdmissionLease) -> SessionStoreResult<()> {
190 management_unsupported()
191 }
192
193 async fn load_run_admission(
195 &self,
196 _target: &crate::ManagedRunTarget,
197 ) -> SessionStoreResult<Option<RunAdmissionLease>> {
198 management_unsupported()
199 }
200
201 async fn reconcile_expired_run_admissions(
203 &self,
204 _namespace_id: &str,
205 _now: chrono::DateTime<chrono::Utc>,
206 ) -> SessionStoreResult<Vec<crate::ManagedRunTarget>> {
207 management_unsupported()
208 }
209
210 async fn load_control_receipt(
212 &self,
213 _target: &crate::ManagedRunTarget,
214 _idempotency_key: &str,
215 ) -> SessionStoreResult<Option<DurableControlReceipt>> {
216 management_unsupported()
217 }
218
219 async fn reserve_control_receipt(
221 &self,
222 _receipt: DurableControlReceipt,
223 ) -> SessionStoreResult<DurableControlReceipt> {
224 management_unsupported()
225 }
226
227 async fn update_control_receipt_state(
229 &self,
230 _receipt_id: &str,
231 _state: &str,
232 ) -> SessionStoreResult<DurableControlReceipt> {
233 management_unsupported()
234 }
235
236 async fn drain_background_subagent_operations(&self) -> SessionStoreResult<()> {
242 management_unsupported()
243 }
244
245 async fn record_background_subagent_acceptance(
247 &self,
248 _record: BackgroundSubagentRecord,
249 ) -> SessionStoreResult<BackgroundSubagentRecord> {
250 management_unsupported()
251 }
252
253 async fn update_background_subagent_execution(
255 &self,
256 _record: BackgroundSubagentRecord,
257 ) -> SessionStoreResult<BackgroundSubagentRecord> {
258 management_unsupported()
259 }
260
261 async fn heartbeat_background_subagent(
263 &self,
264 _attempt_id: &starweaver_core::SubagentAttemptId,
265 _host_instance_id: &str,
266 _fencing_generation: u64,
267 _lease_expires_at: chrono::DateTime<chrono::Utc>,
268 ) -> SessionStoreResult<BackgroundSubagentRecord> {
269 management_unsupported()
270 }
271
272 async fn commit_background_subagent_terminal(
274 &self,
275 commit: crate::BackgroundSubagentTerminalCommit,
276 ) -> SessionStoreResult<BackgroundSubagentRecord> {
277 if commit.artifact.is_some() {
278 return management_unsupported();
279 }
280 self.record_background_subagent_terminal(commit.record)
281 .await
282 }
283
284 async fn load_background_subagent_artifact(
286 &self,
287 _artifact_ref: &str,
288 ) -> SessionStoreResult<crate::BackgroundSubagentArtifact> {
289 management_unsupported()
290 }
291
292 async fn expire_background_subagent_retention(
294 &self,
295 _namespace_id: &str,
296 _now: chrono::DateTime<chrono::Utc>,
297 _limit: usize,
298 ) -> SessionStoreResult<Vec<BackgroundSubagentRecord>> {
299 management_unsupported()
300 }
301
302 async fn record_background_subagent_terminal(
304 &self,
305 _record: BackgroundSubagentRecord,
306 ) -> SessionStoreResult<BackgroundSubagentRecord> {
307 management_unsupported()
308 }
309
310 async fn load_background_subagent(
312 &self,
313 _attempt_id: &starweaver_core::SubagentAttemptId,
314 ) -> SessionStoreResult<BackgroundSubagentRecord> {
315 management_unsupported()
316 }
317
318 async fn list_background_subagents(
320 &self,
321 _namespace_id: &str,
322 _session_id: Option<&SessionId>,
323 _limit: usize,
324 ) -> SessionStoreResult<Vec<BackgroundSubagentRecord>> {
325 management_unsupported()
326 }
327
328 async fn list_pending_background_subagents(
330 &self,
331 _namespace_id: &str,
332 _session_id: Option<&SessionId>,
333 _limit: usize,
334 ) -> SessionStoreResult<Vec<BackgroundSubagentRecord>> {
335 management_unsupported()
336 }
337
338 async fn claim_background_subagent_delivery(
340 &self,
341 _attempt_id: &starweaver_core::SubagentAttemptId,
342 _claim: DurableBackgroundSubagentDeliveryClaim,
343 ) -> SessionStoreResult<BackgroundSubagentRecord> {
344 management_unsupported()
345 }
346
347 async fn acknowledge_background_subagent_delivery(
349 &self,
350 _attempt_id: &starweaver_core::SubagentAttemptId,
351 _claim_id: &str,
352 ) -> SessionStoreResult<BackgroundSubagentRecord> {
353 management_unsupported()
354 }
355
356 async fn release_background_subagent_delivery(
358 &self,
359 _attempt_id: &starweaver_core::SubagentAttemptId,
360 _claim_id: &str,
361 _release: DurableBackgroundSubagentDeliveryRelease,
362 ) -> SessionStoreResult<BackgroundSubagentRecord> {
363 management_unsupported()
364 }
365
366 async fn acquire_background_subagent_continuation(
368 &self,
369 _request: AcquireBackgroundSubagentContinuation,
370 ) -> SessionStoreResult<BackgroundSubagentContinuationReceipt> {
371 management_unsupported()
372 }
373
374 async fn reconcile_background_subagents(
376 &self,
377 _namespace_id: &str,
378 _now: chrono::DateTime<chrono::Utc>,
379 ) -> SessionStoreResult<Vec<BackgroundSubagentRecord>> {
380 management_unsupported()
381 }
382
383 async fn save_session(&self, session: SessionRecord) -> SessionStoreResult<()>;
385
386 async fn load_session(&self, session_id: &SessionId) -> SessionStoreResult<SessionRecord>;
388
389 async fn list_sessions(&self, filter: SessionFilter) -> SessionStoreResult<Vec<SessionRecord>>;
391
392 async fn update_session_status(
394 &self,
395 session_id: &SessionId,
396 status: SessionStatus,
397 ) -> SessionStoreResult<()>;
398
399 async fn save_context_state(
401 &self,
402 session_id: &SessionId,
403 state: ResumableState,
404 ) -> SessionStoreResult<()>;
405
406 async fn save_environment_state(
408 &self,
409 session_id: &SessionId,
410 environment_state: EnvironmentStateRef,
411 ) -> SessionStoreResult<()>;
412
413 async fn append_run(&self, run: RunRecord) -> SessionStoreResult<()>;
418
419 async fn load_run(
421 &self,
422 session_id: &SessionId,
423 run_id: &RunId,
424 ) -> SessionStoreResult<RunRecord>;
425
426 async fn list_runs(&self, session_id: &SessionId) -> SessionStoreResult<Vec<RunRecord>>;
428
429 async fn update_run_status(
431 &self,
432 session_id: &SessionId,
433 run_id: &RunId,
434 status: RunStatus,
435 output_preview: Option<String>,
436 ) -> SessionStoreResult<()>;
437
438 async fn append_checkpoint(
440 &self,
441 session_id: &SessionId,
442 checkpoint: AgentCheckpoint,
443 ) -> SessionStoreResult<()>;
444
445 async fn load_checkpoints(
447 &self,
448 session_id: &SessionId,
449 run_id: &RunId,
450 ) -> SessionStoreResult<Vec<AgentCheckpoint>>;
451
452 async fn latest_checkpoint(
454 &self,
455 session_id: &SessionId,
456 run_id: &RunId,
457 ) -> SessionStoreResult<Option<AgentCheckpoint>> {
458 let checkpoints = self.load_checkpoints(session_id, run_id).await?;
459 Ok(checkpoints.into_iter().last())
460 }
461
462 async fn append_stream_records(
464 &self,
465 session_id: &SessionId,
466 run_id: &RunId,
467 records: Vec<AgentStreamRecord>,
468 ) -> SessionStoreResult<()>;
469
470 async fn replay_stream_records(
472 &self,
473 session_id: &SessionId,
474 run_id: &RunId,
475 ) -> SessionStoreResult<Vec<AgentStreamRecord>>;
476
477 async fn replay_stream_records_after(
479 &self,
480 session_id: &SessionId,
481 run_id: &RunId,
482 after_sequence: Option<usize>,
483 ) -> SessionStoreResult<Vec<AgentStreamRecord>> {
484 let records = self.replay_stream_records(session_id, run_id).await?;
485 Ok(records
486 .into_iter()
487 .filter(|record| after_sequence.is_none_or(|cursor| record.sequence > cursor))
488 .collect())
489 }
490
491 async fn save_stream_cursor(
493 &self,
494 session_id: &SessionId,
495 run_id: &RunId,
496 cursor: StreamCursorRef,
497 ) -> SessionStoreResult<()>;
498
499 async fn append_approval(&self, approval: ApprovalRecord) -> SessionStoreResult<()>;
501
502 async fn load_approvals(
504 &self,
505 session_id: &SessionId,
506 run_id: &RunId,
507 ) -> SessionStoreResult<Vec<ApprovalRecord>>;
508
509 async fn append_deferred_tool(&self, record: DeferredToolRecord) -> SessionStoreResult<()>;
511
512 async fn load_deferred_tools(
514 &self,
515 session_id: &SessionId,
516 run_id: &RunId,
517 ) -> SessionStoreResult<Vec<DeferredToolRecord>>;
518
519 async fn resume_snapshot(
521 &self,
522 _session_id: &SessionId,
523 _run_id: &RunId,
524 ) -> SessionStoreResult<SessionResumeSnapshot> {
525 Err(SessionStoreError::Failed(
526 "session store does not support per-run resume snapshots".to_string(),
527 ))
528 }
529
530 async fn compact_run_trace(
532 &self,
533 session_id: &SessionId,
534 run_id: &RunId,
535 ) -> SessionStoreResult<CompactRunTrace>;
536
537 async fn compact_session_trace(
539 &self,
540 session_id: &SessionId,
541 ) -> SessionStoreResult<CompactSessionTrace>;
542}