@@ -183,6 +183,7 @@ async fn run_batch(
183183 let total_tasks = archive. tasks . len ( ) ;
184184 let agent_code = Arc :: new ( archive. agent_code ) ;
185185 let agent_language = Arc :: new ( archive. agent_language ) ;
186+ let agent_archive = Arc :: new ( archive. agent_archive ) ;
186187 let agent_env = Arc :: new ( agent_env) ;
187188
188189 {
@@ -214,6 +215,7 @@ async fn run_batch(
214215 let events_tx = batch. events_tx . clone ( ) ;
215216 let agent_code = agent_code. clone ( ) ;
216217 let agent_language = agent_language. clone ( ) ;
218+ let agent_archive = agent_archive. clone ( ) ;
217219 let agent_env = agent_env. clone ( ) ;
218220 let semaphore = semaphore. clone ( ) ;
219221 let task_results = task_results. clone ( ) ;
@@ -245,6 +247,7 @@ async fn run_batch(
245247 & task,
246248 & agent_code,
247249 & agent_language,
250+ agent_archive. as_deref ( ) ,
248251 & agent_env,
249252 cancel_rx,
250253 )
@@ -303,6 +306,7 @@ async fn run_single_task(
303306 task : & SweForgeTask ,
304307 agent_code : & str ,
305308 agent_language : & str ,
309+ agent_archive : Option < & [ u8 ] > ,
306310 agent_env : & HashMap < String , String > ,
307311 cancel_rx : tokio:: sync:: watch:: Receiver < bool > ,
308312) -> TaskResult {
@@ -321,6 +325,7 @@ async fn run_single_task(
321325 task,
322326 agent_code,
323327 agent_language,
328+ agent_archive,
324329 agent_env,
325330 & work_dir,
326331 & cancel_rx,
@@ -350,6 +355,7 @@ async fn run_task_pipeline(
350355 task : & SweForgeTask ,
351356 agent_code : & str ,
352357 agent_language : & str ,
358+ agent_archive : Option < & [ u8 ] > ,
353359 agent_env : & HashMap < String , String > ,
354360 work_dir : & Path ,
355361 cancel_rx : & tokio:: sync:: watch:: Receiver < bool > ,
@@ -411,6 +417,7 @@ async fn run_task_pipeline(
411417 let agent_output = run_agent (
412418 agent_code,
413419 agent_language,
420+ agent_archive,
414421 & task. prompt ,
415422 & repo_dir,
416423 config. agent_timeout_secs ,
@@ -548,29 +555,95 @@ fn agent_runner(language: &str, script_path: &str) -> Vec<String> {
548555async fn run_agent (
549556 agent_code : & str ,
550557 agent_language : & str ,
558+ agent_archive : Option < & [ u8 ] > ,
551559 prompt : & str ,
552560 repo_dir : & Path ,
553561 timeout_secs : u64 ,
554562 agent_env : & HashMap < String , String > ,
555563) -> Result < String > {
556- let ext = agent_extension ( agent_language) ;
557- let script_name = format ! ( "_agent_code{}" , ext) ;
558- let script_path = repo_dir. join ( & script_name) ;
559- tokio:: fs:: write ( & script_path, agent_code) . await ?;
560-
561564 let prompt_path = repo_dir. join ( "_task_prompt.md" ) ;
562565 tokio:: fs:: write ( & prompt_path, prompt) . await ?;
563566
564- let mut argv_owned = agent_runner ( agent_language, & script_name) ;
565- // Pass --instruction for Python agents (baseagent convention)
566- if matches ! ( agent_language. to_lowercase( ) . as_str( ) , "python" | "py" ) {
567- argv_owned. push ( "--instruction" . into ( ) ) ;
568- argv_owned. push ( prompt. into ( ) ) ;
569- }
567+ // If we have the full archive, extract it into the repo so the agent project
568+ // structure (agent_code/agent.py, requirements.txt, src/, etc.) is preserved.
569+ let ( argv_owned, run_dir) = if let Some ( archive_bytes) = agent_archive {
570+ let agent_base = repo_dir. join ( "_agent" ) ;
571+ let _ = tokio:: fs:: create_dir_all ( & agent_base) . await ;
572+ let base = agent_base. clone ( ) ;
573+ let data = archive_bytes. to_vec ( ) ;
574+ tokio:: task:: spawn_blocking ( move || crate :: task:: extract_archive_bytes ( & data, & base) )
575+ . await
576+ . context ( "extract agent archive" ) ??;
577+
578+ // Find agent_code/ dir inside extracted archive
579+ let agent_dir = if agent_base. join ( "agent_code" ) . exists ( ) {
580+ agent_base. join ( "agent_code" )
581+ } else {
582+ // Look one level deeper
583+ let mut found = agent_base. clone ( ) ;
584+ if let Ok ( mut entries) = tokio:: fs:: read_dir ( & agent_base) . await {
585+ while let Ok ( Some ( entry) ) = entries. next_entry ( ) . await {
586+ if entry. path ( ) . join ( "agent_code" ) . exists ( ) {
587+ found = entry. path ( ) . join ( "agent_code" ) ;
588+ break ;
589+ }
590+ }
591+ }
592+ found
593+ } ;
594+
595+ // Install Python dependencies if requirements.txt exists
596+ if agent_dir. join ( "requirements.txt" ) . exists ( ) {
597+ info ! ( "Installing agent requirements.txt" ) ;
598+ let ( _, stderr, exit) = run_shell (
599+ "pip install -q -r requirements.txt 2>&1 || pip3 install -q -r requirements.txt 2>&1 || true" ,
600+ & agent_dir,
601+ Duration :: from_secs ( 120 ) ,
602+ None ,
603+ )
604+ . await ?;
605+ if exit != 0 {
606+ warn ! (
607+ "Agent pip install failed (exit {}): {}" ,
608+ exit,
609+ & stderr[ ..stderr. len( ) . min( 500 ) ]
610+ ) ;
611+ }
612+ }
613+
614+ // Determine entry point
615+ let entry = if agent_dir. join ( "agent.py" ) . exists ( ) {
616+ "agent.py"
617+ } else if agent_dir. join ( "main.py" ) . exists ( ) {
618+ "main.py"
619+ } else {
620+ "agent.py"
621+ } ;
622+
623+ let mut argv = vec ! [ "python3" . to_string( ) , entry. to_string( ) ] ;
624+ argv. push ( "--instruction" . into ( ) ) ;
625+ argv. push ( prompt. into ( ) ) ;
626+ ( argv, agent_dir)
627+ } else {
628+ // Legacy path: single-file agent code written to _agent_code.py
629+ let ext = agent_extension ( agent_language) ;
630+ let script_name = format ! ( "_agent_code{}" , ext) ;
631+ let script_path = repo_dir. join ( & script_name) ;
632+ tokio:: fs:: write ( & script_path, agent_code) . await ?;
633+
634+ let mut argv = agent_runner ( agent_language, & script_name) ;
635+ if matches ! ( agent_language. to_lowercase( ) . as_str( ) , "python" | "py" ) {
636+ argv. push ( "--instruction" . into ( ) ) ;
637+ argv. push ( prompt. into ( ) ) ;
638+ }
639+ ( argv, repo_dir. to_path_buf ( ) )
640+ } ;
641+
570642 let argv: Vec < & str > = argv_owned. iter ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ;
571643 info ! (
572- "Running agent: {:?} with {} env vars" ,
644+ "Running agent: {:?} in {} with {} env vars" ,
573645 argv,
646+ run_dir. display( ) ,
574647 agent_env. len( )
575648 ) ;
576649
@@ -591,7 +664,7 @@ async fn run_agent(
591664
592665 let ( stdout, stderr, exit) = run_cmd (
593666 & argv,
594- repo_dir ,
667+ & run_dir ,
595668 Duration :: from_secs ( timeout_secs) ,
596669 Some ( & env_refs) ,
597670 )
0 commit comments