1111 cleanup_agent_workflows ,
1212 should_cleanup_on_restart
1313)
14+ from agentex .lib .cli .utils .path_utils import (
15+ get_file_paths ,
16+ calculate_uvicorn_target_for_local ,
17+ )
18+
1419from agentex .lib .environment_variables import EnvVarKeys
1520from agentex .lib .sdk .config .agent_manifest import AgentManifest
1621from agentex .lib .utils .logging import make_logger
@@ -104,7 +109,10 @@ async def start_worker() -> asyncio.subprocess.Process:
104109 # PRE-RESTART CLEANUP - NEW!
105110 if current_process is not None :
106111 # Extract agent name from worker path for cleanup
107- agent_name = worker_path .parent .parent .name
112+
113+ agent_name = env .get ("AGENT_NAME" )
114+ if agent_name is None :
115+ agent_name = worker_path .parent .parent .name
108116
109117 # Perform cleanup if configured
110118 if should_cleanup_on_restart ():
@@ -180,15 +188,17 @@ async def start_worker() -> asyncio.subprocess.Process:
180188
181189
182190async def start_acp_server (
183- acp_path : Path , port : int , env : dict [str , str ]
191+ acp_path : Path , port : int , env : dict [str , str ], manifest_dir : Path
184192) -> asyncio .subprocess .Process :
185193 """Start the ACP server process"""
186- # Use the actual file path instead of module path for better reload detection
194+ # Use file path relative to manifest directory if possible
195+ uvicorn_target = calculate_uvicorn_target_for_local (acp_path , manifest_dir )
196+
187197 cmd = [
188198 sys .executable ,
189199 "-m" ,
190200 "uvicorn" ,
191- f"{ acp_path . parent . name } .acp :acp" ,
201+ f"{ uvicorn_target } :acp" ,
192202 "--reload" ,
193203 "--reload-dir" ,
194204 str (acp_path .parent ), # Watch the project directory specifically
@@ -201,7 +211,7 @@ async def start_acp_server(
201211 console .print (f"[blue]Starting ACP server from { acp_path } on port { port } ...[/blue]" )
202212 return await asyncio .create_subprocess_exec (
203213 * cmd ,
204- cwd = acp_path . parent . parent ,
214+ cwd = manifest_dir , # Always use manifest directory as CWD for consistency
205215 env = env ,
206216 stdout = asyncio .subprocess .PIPE ,
207217 stderr = asyncio .subprocess .STDOUT ,
@@ -218,7 +228,7 @@ async def start_temporal_worker(
218228
219229 return await asyncio .create_subprocess_exec (
220230 * cmd ,
221- cwd = worker_path .parent ,
231+ cwd = worker_path .parent , # Use worker directory as CWD for imports to work
222232 env = env ,
223233 stdout = asyncio .subprocess .PIPE ,
224234 stderr = asyncio .subprocess .STDOUT ,
@@ -280,8 +290,9 @@ async def run_agent(manifest_path: str):
280290 )
281291
282292 # Start ACP server
293+ manifest_dir = Path (manifest_path ).parent
283294 acp_process = await start_acp_server (
284- file_paths ["acp" ], manifest .local_development .agent .port , agent_env
295+ file_paths ["acp" ], manifest .local_development .agent .port , agent_env , manifest_dir
285296 )
286297 process_manager .add_process (acp_process )
287298
@@ -291,7 +302,7 @@ async def run_agent(manifest_path: str):
291302 tasks = [acp_output_task ]
292303
293304 # Start temporal worker if needed
294- if is_temporal_agent (manifest ):
305+ if is_temporal_agent (manifest ) and file_paths [ "worker" ] :
295306 worker_task = await start_temporal_worker_with_reload (file_paths ["worker" ], agent_env , process_manager )
296307 tasks .append (worker_task )
297308
@@ -323,92 +334,7 @@ async def run_agent(manifest_path: str):
323334 await process_manager .cleanup_processes ()
324335
325336
326- def resolve_and_validate_path (base_path : Path , configured_path : str , file_type : str ) -> Path :
327- """Resolve and validate a configured path"""
328- path_obj = Path (configured_path )
329-
330- if path_obj .is_absolute ():
331- # Absolute path - use as-is
332- resolved_path = path_obj
333- else :
334- # Relative path - resolve relative to manifest directory
335- resolved_path = (base_path / configured_path ).resolve ()
336-
337- # Validate the file exists
338- if not resolved_path .exists ():
339- raise RunError (
340- f"{ file_type } file not found: { resolved_path } \n "
341- f" Configured path: { configured_path } \n "
342- f" Resolved from manifest: { base_path } "
343- )
344-
345- # Validate it's actually a file
346- if not resolved_path .is_file ():
347- raise RunError (f"{ file_type } path is not a file: { resolved_path } " )
348-
349- return resolved_path
350-
351-
352- def validate_path_security (resolved_path : Path , manifest_dir : Path ) -> None :
353- """Basic security validation for resolved paths"""
354- try :
355- # Ensure the resolved path is accessible
356- resolved_path .resolve ()
357-
358- # Optional: Add warnings for paths that go too far up
359- try :
360- # Check if path goes more than 3 levels up from manifest
361- relative_to_manifest = resolved_path .relative_to (manifest_dir .parent .parent .parent )
362- if str (relative_to_manifest ).startswith (".." ):
363- logger .warning (
364- f"Path goes significantly outside project structure: { resolved_path } "
365- )
366- except ValueError :
367- # Path is outside the tree - that's okay, just log it
368- logger .info (f"Using path outside manifest directory tree: { resolved_path } " )
369-
370- except Exception as e :
371- raise RunError (f"Path resolution failed: { resolved_path } - { str (e )} " ) from e
372-
373337
374- def get_file_paths (manifest : AgentManifest , manifest_path : str ) -> dict [str , Path ]:
375- """Get resolved file paths from manifest configuration"""
376- manifest_dir = Path (manifest_path ).parent .resolve ()
377-
378- # Use configured paths or fall back to defaults for backward compatibility
379- if manifest .local_development and manifest .local_development .paths :
380- paths_config = manifest .local_development .paths
381-
382- # Resolve ACP path
383- acp_path = resolve_and_validate_path (manifest_dir , paths_config .acp , "ACP server" )
384- validate_path_security (acp_path , manifest_dir )
385-
386- # Resolve worker path if specified
387- worker_path = None
388- if paths_config .worker :
389- worker_path = resolve_and_validate_path (
390- manifest_dir , paths_config .worker , "Temporal worker"
391- )
392- validate_path_security (worker_path , manifest_dir )
393- else :
394- # Backward compatibility: use old hardcoded structure
395- project_dir = manifest_dir / "project"
396- acp_path = project_dir / "acp.py"
397- worker_path = project_dir / "run_worker.py" if is_temporal_agent (manifest ) else None
398-
399- # Validate backward compatibility paths
400- if not acp_path .exists ():
401- raise RunError (f"ACP file not found: { acp_path } " )
402-
403- if worker_path and not worker_path .exists ():
404- raise RunError (f"Worker file not found: { worker_path } " )
405-
406- return {
407- "acp" : acp_path ,
408- "worker" : worker_path ,
409- "acp_dir" : acp_path .parent ,
410- "worker_dir" : worker_path .parent if worker_path else None ,
411- }
412338
413339
414340def create_agent_environment (manifest : AgentManifest ) -> dict [str , str ]:
0 commit comments