1use super::{Agent, AgentFailure, AgentRunTime, FailureKind, storage_failure};
2use crate::{
3 error::AgentError,
4 shared::{Message, Role},
5 state::AgentState,
6 trajectory::{TrajectoryEvent, TrajectoryEventKind},
7 transcript::Transcript,
8};
9use tokio_util::sync::CancellationToken;
10use uuid::Uuid;
11
12impl AgentRunTime {
13 pub async fn run(
19 &self,
20 agent_state: Agent,
21 cancellation_token: CancellationToken,
22 ) -> Result<Agent, AgentError> {
23 let session_id = self.get_session_id(&agent_state);
24 let session_lock = self.session_lock(&session_id).await;
25 let _guard = session_lock.lock().await;
26 self.run_unlocked(agent_state, cancellation_token).await
27 }
28
29 async fn run_unlocked(
30 &self,
31 agent_state: Agent,
32 cancellation_token: CancellationToken,
33 ) -> Result<Agent, AgentError> {
34 let outcome = self.run_inner(agent_state, cancellation_token).await?;
35 if matches!(outcome, Agent::Success(_) | Agent::Fail(_, _)) {
36 let session_id = self.get_session_id(&outcome);
37 if let Err(e) = self.checkpoint_storage.delete_checkpoint(&session_id).await {
38 return Ok(storage_failure(AgentState::new(Some(session_id)), e));
39 }
40 }
41 Ok(outcome)
42 }
43
44 pub async fn resume(
49 &self,
50 session_id: &str,
51 cancellation_token: CancellationToken,
52 ) -> Result<Option<Agent>, AgentError> {
53 let session_lock = self.session_lock(session_id).await;
54 let _guard = session_lock.lock().await;
55 let checkpoint = match self.checkpoint_storage.get_checkpoint(session_id).await {
56 Ok(Some(checkpoint)) => checkpoint,
57 Ok(None) => return Ok(None),
58 Err(e) => {
59 return Ok(Some(storage_failure(
60 AgentState::new(Some(session_id.to_string())),
61 e,
62 )));
63 }
64 };
65 self.run_unlocked(checkpoint, cancellation_token)
66 .await
67 .map(Some)
68 }
69
70 pub async fn run_session(
73 &self,
74 session_id: &str,
75 cancellation_token: CancellationToken,
76 ) -> Result<Agent, AgentError> {
77 let session_lock = self.session_lock(session_id).await;
78 let _guard = session_lock.lock().await;
79 let start = match self.checkpoint_storage.get_checkpoint(session_id).await {
80 Ok(Some(checkpoint)) => checkpoint,
81 Ok(None) => Agent::Ready(AgentState::new(Some(session_id.to_string()))),
82 Err(e) => {
83 return Ok(storage_failure(
84 AgentState::new(Some(session_id.to_string())),
85 e,
86 ));
87 }
88 };
89 self.run_unlocked(start, cancellation_token).await
90 }
91
92 async fn run_inner(
93 &self,
94 mut agent_state: Agent,
95 cancellation_token: CancellationToken,
96 ) -> Result<Agent, AgentError> {
97 loop {
98 if cancellation_token.is_cancelled()
102 && !matches!(agent_state, Agent::ToolExecuting(_, _))
103 {
104 let session_id = self.get_session_id(&agent_state);
105 let parked = match agent_state {
106 Agent::Reasoning(ctx) => Agent::Interrupted(ctx),
107 other => other,
108 };
109 if let Err(e) = self
110 .checkpoint_storage
111 .as_ref()
112 .save_checkpoint(&session_id, &parked)
113 .await
114 {
115 return Ok(storage_failure(AgentState::new(Some(session_id)), e));
116 }
117 self.emit(&session_id, TrajectoryEventKind::Interrupted)
118 .await;
119 return Ok(parked);
120 }
121
122 agent_state = match agent_state {
123 Agent::Interrupted(ctx) => Agent::Ready(ctx),
124 Agent::Ready(mut ctx) => {
125 if ctx.steps_count >= self.max_iter {
127 let failure = AgentFailure::new(
128 FailureKind::MaxIter,
129 format!("exceeded max_iter ({})", self.max_iter),
130 );
131 self.emit(
132 &ctx.session_id,
133 TrajectoryEventKind::Failed {
134 kind: failure.kind.as_str().to_string(),
135 message: failure.message.clone(),
136 },
137 )
138 .await;
139 return Ok(Agent::Fail(ctx, failure));
140 }
141 ctx.steps_count += 1;
142 Agent::Reasoning(ctx)
143 }
144 Agent::Reasoning(ctx) => {
145 self.handle_reasoning(ctx, cancellation_token.clone())
146 .await?
147 }
148 Agent::ToolExecuting(ctx, tool_calls) => {
149 self.execute_pending_tools(&ctx, tool_calls, cancellation_token.clone())
150 .await?
151 }
152 Agent::Fail(ctx, failure) => match self.handle_fail(ctx, failure).await? {
153 ready @ Agent::Ready(_) => ready,
154 terminal => return Ok(terminal),
155 },
156 other => return Ok(other),
157 }
158 }
159 }
160
161 pub async fn create_session(&self) -> Result<String, AgentError> {
162 let new_session_id = Uuid::new_v4().to_string();
163 let transcript = Transcript::new(&new_session_id);
164 self.transcript_storage
165 .save_transcript(&new_session_id, &transcript)
166 .await?;
167 let sandbox = self.sandbox.clone().open(&new_session_id).await?;
168 sandbox.save().await?;
169 Ok(new_session_id)
170 }
171
172 pub async fn submit(
175 &self,
176 session_id: &str,
177 user_text: impl Into<String>,
178 ) -> Result<Agent, AgentError> {
179 let session_lock = self.session_lock(session_id).await;
180 let _guard = session_lock.lock().await;
181 if let Err(e) = self.checkpoint_storage.delete_checkpoint(session_id).await {
182 return Ok(storage_failure(
183 AgentState::new(Some(session_id.to_string())),
184 e,
185 ));
186 }
187 let user_text = user_text.into();
188 if let Err(e) = self
189 .transcript_storage
190 .append_message(session_id, Message::text(Role::User, user_text.clone()))
191 .await
192 {
193 return Ok(storage_failure(
194 AgentState::new(Some(session_id.to_string())),
195 e,
196 ));
197 }
198 self.emit(
199 session_id,
200 TrajectoryEventKind::UserMessage { text: user_text },
201 )
202 .await;
203 Ok(Agent::Ready(AgentState::new(Some(session_id.to_string()))))
204 }
205
206 async fn session_lock(&self, session_id: &str) -> std::sync::Arc<tokio::sync::Mutex<()>> {
207 let mut locks = self.session_locks.lock().await;
208 locks.retain(|_, lock| lock.strong_count() > 0);
209 if let Some(lock) = locks.get(session_id).and_then(|lock| lock.upgrade()) {
210 return lock;
211 }
212 let lock = std::sync::Arc::new(tokio::sync::Mutex::new(()));
213 locks.insert(session_id.to_string(), std::sync::Arc::downgrade(&lock));
214 lock
215 }
216
217 pub(crate) fn get_session_id(&self, agent: &Agent) -> String {
218 match agent {
219 Agent::Fail(ctx, _)
220 | Agent::Interrupted(ctx)
221 | Agent::Ready(ctx)
222 | Agent::Reasoning(ctx)
223 | Agent::Success(ctx)
224 | Agent::ToolExecuting(ctx, _)
225 | Agent::WaitingForUser(ctx, _) => ctx.session_id.clone(),
226 }
227 }
228
229 pub(crate) async fn interrupt(&self, ctx: &AgentState) -> Result<Agent, AgentError> {
233 let agent = Agent::Interrupted(ctx.clone());
234 if let Err(e) = self
235 .checkpoint_storage
236 .as_ref()
237 .save_checkpoint(&ctx.session_id, &agent)
238 .await
239 {
240 return Ok(storage_failure(ctx.clone(), e));
241 }
242 self.emit(&ctx.session_id, TrajectoryEventKind::Interrupted)
243 .await;
244 Ok(agent)
245 }
246
247 pub(crate) async fn save_interruption(&self, agent: &Agent) -> Result<(), AgentError> {
248 let session_id = self.get_session_id(agent);
249 self.checkpoint_storage
250 .as_ref()
251 .save_checkpoint(&session_id, agent)
252 .await?;
253 self.emit(&session_id, TrajectoryEventKind::Interrupted)
254 .await;
255 Ok(())
256 }
257
258 pub(crate) async fn emit(&self, session_id: &str, kind: TrajectoryEventKind) {
260 self.trajectory
261 .emit(TrajectoryEvent::new(session_id, kind))
262 .await;
263 }
264}