agtrace_runtime/client/
sessions.rs1use crate::ops::{
2 ExportService, IndexProgress, IndexService, ListSessionsRequest, PackResult, PackService,
3 SessionService,
4};
5use crate::storage::{RawFileContent, get_raw_files};
6use crate::{Error, Result};
7use agtrace_engine::export::ExportStrategy;
8use agtrace_index::{Database, SessionSummary};
9use agtrace_providers::ProviderAdapter;
10use agtrace_types::AgentEvent;
11use std::path::PathBuf;
12use std::sync::{Arc, Mutex};
13
14#[derive(Debug, Clone)]
15pub struct SessionFilter {
16 pub scope: agtrace_types::ProjectScope,
17 pub limit: usize,
18 pub provider: Option<String>,
19 pub since: Option<String>,
20 pub until: Option<String>,
21}
22
23impl SessionFilter {
24 pub fn all() -> Self {
26 Self {
27 scope: agtrace_types::ProjectScope::All,
28 limit: 100,
29 provider: None,
30 since: None,
31 until: None,
32 }
33 }
34
35 pub fn project(project_hash: agtrace_types::ProjectHash) -> Self {
37 Self {
38 scope: agtrace_types::ProjectScope::Specific(project_hash),
39 limit: 100,
40 provider: None,
41 since: None,
42 until: None,
43 }
44 }
45
46 pub fn limit(mut self, limit: usize) -> Self {
47 self.limit = limit;
48 self
49 }
50
51 pub fn provider(mut self, provider: String) -> Self {
52 self.provider = Some(provider);
53 self
54 }
55
56 pub fn since(mut self, since: String) -> Self {
57 self.since = Some(since);
58 self
59 }
60
61 pub fn until(mut self, until: String) -> Self {
62 self.until = Some(until);
63 self
64 }
65}
66
67pub struct SessionOps {
68 db: Arc<Mutex<Database>>,
69 provider_configs: Arc<Vec<(String, PathBuf)>>,
70}
71
72impl SessionOps {
73 pub fn new(db: Arc<Mutex<Database>>, provider_configs: Arc<Vec<(String, PathBuf)>>) -> Self {
74 Self {
75 db,
76 provider_configs,
77 }
78 }
79
80 pub fn list(&self, filter: SessionFilter) -> Result<Vec<SessionSummary>> {
81 self.ensure_index_is_fresh()?;
82 self.list_without_refresh(filter)
83 }
84
85 pub fn list_without_refresh(&self, filter: SessionFilter) -> Result<Vec<SessionSummary>> {
86 let db = self.db.lock().unwrap();
87 let service = SessionService::new(&db);
88 let request = ListSessionsRequest {
89 scope: filter.scope,
90 limit: filter.limit,
91 provider: filter.provider,
92 since: filter.since,
93 until: filter.until,
94 };
95 service.list_sessions(request)
96 }
97
98 fn ensure_index_is_fresh(&self) -> Result<()> {
99 let db = self.db.lock().unwrap();
100
101 let providers: Vec<(ProviderAdapter, PathBuf)> = self
102 .provider_configs
103 .iter()
104 .filter_map(|(name, path)| {
105 agtrace_providers::create_adapter(name)
106 .ok()
107 .map(|p| (p, path.clone()))
108 })
109 .collect();
110
111 let service = IndexService::new(&db, providers);
112
113 let scope = agtrace_types::ProjectScope::All;
115
116 service.run(scope, false, |_progress: IndexProgress| {})?;
117
118 Ok(())
119 }
120
121 pub fn find(&self, session_id: &str) -> Result<SessionHandle> {
122 if let Some(resolved_id) = self.resolve_session_id(session_id)? {
123 return Ok(SessionHandle {
124 id: resolved_id,
125 db: self.db.clone(),
126 });
127 }
128
129 self.ensure_index_is_fresh()?;
130
131 if let Some(resolved_id) = self.resolve_session_id(session_id)? {
132 return Ok(SessionHandle {
133 id: resolved_id,
134 db: self.db.clone(),
135 });
136 }
137
138 Err(Error::InvalidOperation(format!(
139 "Session not found: {}",
140 session_id
141 )))
142 }
143
144 fn resolve_session_id(&self, session_id: &str) -> Result<Option<String>> {
145 let db = self.db.lock().unwrap();
146
147 if let Some(session) = db.get_session_by_id(session_id)? {
148 return Ok(Some(session.id));
149 }
150
151 Ok(db.find_session_by_prefix(session_id)?)
152 }
153
154 pub fn pack_context(
155 &self,
156 project_hash: Option<&agtrace_types::ProjectHash>,
157 limit: usize,
158 ) -> Result<PackResult> {
159 self.ensure_index_is_fresh()?;
160
161 let db = self.db.lock().unwrap();
162 let service = PackService::new(&db);
163 service.select_sessions(project_hash, limit)
164 }
165}
166
167pub struct SessionHandle {
168 id: String,
169 db: Arc<Mutex<Database>>,
170}
171
172impl SessionHandle {
173 pub fn events(&self) -> Result<Vec<AgentEvent>> {
174 let db = self.db.lock().unwrap();
175 let service = SessionService::new(&db);
176 service.get_session_events(&self.id)
177 }
178
179 pub fn raw_files(&self) -> Result<Vec<RawFileContent>> {
180 let db = self.db.lock().unwrap();
181 get_raw_files(&db, &self.id)
182 }
183
184 pub fn export(&self, strategy: ExportStrategy) -> Result<Vec<AgentEvent>> {
185 let db = self.db.lock().unwrap();
186 let service = ExportService::new(&db);
187 service.export_session(&self.id, strategy)
188 }
189
190 pub fn id(&self) -> &str {
191 &self.id
192 }
193}