@@ -4,6 +4,8 @@ use std::path::{Path, PathBuf};
44use std:: process:: Stdio ;
55use tokio:: process:: Command ;
66
7+ use super :: output:: SessionOutputStore ;
8+ use super :: runtime:: capture_command_output;
79use super :: store:: StateStore ;
810use super :: { Session , SessionMetrics , SessionState } ;
911use crate :: config:: Config ;
@@ -18,18 +20,7 @@ pub async fn create_session(
1820) -> Result < String > {
1921 let repo_root =
2022 std:: env:: current_dir ( ) . context ( "Failed to resolve current working directory" ) ?;
21- let agent_program = agent_program ( agent_type) ?;
22-
23- create_session_in_dir (
24- db,
25- cfg,
26- task,
27- agent_type,
28- use_worktree,
29- & repo_root,
30- & agent_program,
31- )
32- . await
23+ queue_session_in_dir ( db, cfg, task, agent_type, use_worktree, & repo_root) . await
3324}
3425
3526pub fn list_sessions ( db : & StateStore ) -> Result < Vec < Session > > {
@@ -62,35 +53,107 @@ fn resolve_session(db: &StateStore, id: &str) -> Result<Session> {
6253 session. ok_or_else ( || anyhow:: anyhow!( "Session not found: {id}" ) )
6354}
6455
65- async fn create_session_in_dir (
56+ pub async fn run_session (
57+ cfg : & Config ,
58+ session_id : & str ,
59+ task : & str ,
60+ agent_type : & str ,
61+ working_dir : & Path ,
62+ ) -> Result < ( ) > {
63+ let db = StateStore :: open ( & cfg. db_path ) ?;
64+ let session = resolve_session ( & db, session_id) ?;
65+
66+ if session. state != SessionState :: Pending {
67+ tracing:: info!(
68+ "Skipping run_session for {} because state is {}" ,
69+ session_id,
70+ session. state
71+ ) ;
72+ return Ok ( ( ) ) ;
73+ }
74+
75+ let agent_program = agent_program ( agent_type) ?;
76+ let command = build_agent_command ( & agent_program, task, session_id, working_dir) ;
77+ capture_command_output (
78+ cfg. db_path . clone ( ) ,
79+ session_id. to_string ( ) ,
80+ command,
81+ SessionOutputStore :: default ( ) ,
82+ )
83+ . await ?;
84+ Ok ( ( ) )
85+ }
86+
87+ async fn queue_session_in_dir (
6688 db : & StateStore ,
6789 cfg : & Config ,
6890 task : & str ,
6991 agent_type : & str ,
7092 use_worktree : bool ,
7193 repo_root : & Path ,
72- agent_program : & Path ,
7394) -> Result < String > {
95+ let session = build_session_record ( task, agent_type, use_worktree, cfg, repo_root) ?;
96+ db. insert_session ( & session) ?;
97+
98+ let working_dir = session
99+ . worktree
100+ . as_ref ( )
101+ . map ( |worktree| worktree. path . as_path ( ) )
102+ . unwrap_or ( repo_root) ;
103+
104+ match spawn_session_runner ( task, & session. id , agent_type, working_dir) . await {
105+ Ok ( ( ) ) => Ok ( session. id ) ,
106+ Err ( error) => {
107+ db. update_state ( & session. id , & SessionState :: Failed ) ?;
108+
109+ if let Some ( worktree) = session. worktree . as_ref ( ) {
110+ let _ = crate :: worktree:: remove ( & worktree. path ) ;
111+ }
112+
113+ Err ( error. context ( format ! ( "Failed to queue session {}" , session. id) ) )
114+ }
115+ }
116+ }
117+
118+ fn build_session_record (
119+ task : & str ,
120+ agent_type : & str ,
121+ use_worktree : bool ,
122+ cfg : & Config ,
123+ repo_root : & Path ,
124+ ) -> Result < Session > {
74125 let id = uuid:: Uuid :: new_v4 ( ) . to_string ( ) [ ..8 ] . to_string ( ) ;
75126 let now = chrono:: Utc :: now ( ) ;
76127
77- let wt = if use_worktree {
128+ let worktree = if use_worktree {
78129 Some ( worktree:: create_for_session_in_repo ( & id, cfg, repo_root) ?)
79130 } else {
80131 None
81132 } ;
82133
83- let session = Session {
84- id : id . clone ( ) ,
134+ Ok ( Session {
135+ id,
85136 task : task. to_string ( ) ,
86137 agent_type : agent_type. to_string ( ) ,
87138 state : SessionState :: Pending ,
88139 pid : None ,
89- worktree : wt ,
140+ worktree,
90141 created_at : now,
91142 updated_at : now,
92143 metrics : SessionMetrics :: default ( ) ,
93- } ;
144+ } )
145+ }
146+
147+ async fn create_session_in_dir (
148+ db : & StateStore ,
149+ cfg : & Config ,
150+ task : & str ,
151+ agent_type : & str ,
152+ use_worktree : bool ,
153+ repo_root : & Path ,
154+ agent_program : & Path ,
155+ ) -> Result < String > {
156+ let session = build_session_record ( task, agent_type, use_worktree, cfg, repo_root) ?;
94157
95158 db. insert_session ( & session) ?;
96159
@@ -118,19 +181,60 @@ async fn create_session_in_dir(
118181 }
119182}
120183
121- async fn spawn_claude_code (
122- agent_program : & Path ,
184+ async fn spawn_session_runner (
123185 task : & str ,
124186 session_id : & str ,
187+ agent_type : & str ,
125188 working_dir : & Path ,
126- ) -> Result < u32 > {
127- let child = Command :: new ( agent_program)
189+ ) -> Result < ( ) > {
190+ let current_exe = std:: env:: current_exe ( ) . context ( "Failed to resolve ECC executable path" ) ?;
191+ let child = Command :: new ( & current_exe)
192+ . arg ( "run-session" )
193+ . arg ( "--session-id" )
194+ . arg ( session_id)
195+ . arg ( "--task" )
196+ . arg ( task)
197+ . arg ( "--agent" )
198+ . arg ( agent_type)
199+ . arg ( "--cwd" )
200+ . arg ( working_dir)
201+ . stdin ( Stdio :: null ( ) )
202+ . stdout ( Stdio :: null ( ) )
203+ . stderr ( Stdio :: null ( ) )
204+ . spawn ( )
205+ . with_context ( || {
206+ format ! (
207+ "Failed to spawn ECC runner from {}" ,
208+ current_exe. display( )
209+ )
210+ } ) ?;
211+
212+ child
213+ . id ( )
214+ . ok_or_else ( || anyhow:: anyhow!( "ECC runner did not expose a process id" ) ) ?;
215+ Ok ( ( ) )
216+ }
217+
218+ fn build_agent_command ( agent_program : & Path , task : & str , session_id : & str , working_dir : & Path ) -> Command {
219+ let mut command = Command :: new ( agent_program) ;
220+ command
128221 . arg ( "--print" )
129222 . arg ( "--name" )
130223 . arg ( format ! ( "ecc-{session_id}" ) )
131224 . arg ( task)
132225 . current_dir ( working_dir)
133- . stdin ( Stdio :: null ( ) )
226+ . stdin ( Stdio :: null ( ) ) ;
227+ command
228+ }
229+
230+ async fn spawn_claude_code (
231+ agent_program : & Path ,
232+ task : & str ,
233+ session_id : & str ,
234+ working_dir : & Path ,
235+ ) -> Result < u32 > {
236+ let mut command = build_agent_command ( agent_program, task, session_id, working_dir) ;
237+ let child = command
134238 . stdout ( Stdio :: null ( ) )
135239 . stderr ( Stdio :: null ( ) )
136240 . spawn ( )
0 commit comments