Stop the agent and all its child processes (SIGTERM then SIGKILL if needed). CRITICAL: Kills entire process tree to prevent orphaned coding/testing agents. Returns: Tuple of (success, message)
(self)
| 394 | return False, f"Failed to start agent: {e}" |
| 395 | |
| 396 | async def stop(self) -> tuple[bool, str]: |
| 397 | """ |
| 398 | Stop the agent and all its child processes (SIGTERM then SIGKILL if needed). |
| 399 | |
| 400 | CRITICAL: Kills entire process tree to prevent orphaned coding/testing agents. |
| 401 | |
| 402 | Returns: |
| 403 | Tuple of (success, message) |
| 404 | """ |
| 405 | if not self.process or self.status == "stopped": |
| 406 | return False, "Agent is not running" |
| 407 | |
| 408 | try: |
| 409 | # Cancel output streaming |
| 410 | if self._output_task: |
| 411 | self._output_task.cancel() |
| 412 | try: |
| 413 | await self._output_task |
| 414 | except asyncio.CancelledError: |
| 415 | pass |
| 416 | |
| 417 | # CRITICAL: Kill entire process tree, not just orchestrator |
| 418 | # This ensures all spawned coding/testing agents are also terminated |
| 419 | proc = self.process # Capture reference before async call |
| 420 | loop = asyncio.get_running_loop() |
| 421 | result = await loop.run_in_executor(None, kill_process_tree, proc, 10.0) |
| 422 | logger.debug( |
| 423 | "Process tree kill result: status=%s, children=%d (terminated=%d, killed=%d)", |
| 424 | result.status, result.children_found, |
| 425 | result.children_terminated, result.children_killed |
| 426 | ) |
| 427 | |
| 428 | self._remove_lock() |
| 429 | self.status = "stopped" |
| 430 | self.process = None |
| 431 | self.started_at = None |
| 432 | self.yolo_mode = False # Reset YOLO mode |
| 433 | self.model = None # Reset model |
| 434 | self.parallel_mode = False # Reset parallel mode |
| 435 | self.max_concurrency = None # Reset concurrency |
| 436 | self.testing_agent_ratio = 1 # Reset testing ratio |
| 437 | |
| 438 | return True, "Agent stopped" |
| 439 | except Exception as e: |
| 440 | logger.exception("Failed to stop agent") |
| 441 | return False, f"Failed to stop agent: {e}" |
| 442 | |
| 443 | async def pause(self) -> tuple[bool, str]: |
| 444 | """ |
no test coverage detected