fix(exec): terminate one-shot process trees on cleanup

Run one-shot commands in their own process tree and terminate all
descendants after timeout, cancellation, or unexpected failures.

Co-authored-by: TRAE CLI <noreply@bytedance.com>
This commit is contained in:
yorkhellen
2026-08-12 14:37:27 +08:00
committed by chengyongru
co-authored by TRAE CLI
parent abfcdd481a
commit d64b84604c
3 changed files with 65 additions and 20 deletions
+4 -3
View File
@@ -326,6 +326,7 @@ class ExecTool(Tool):
prepared.env,
prepared.shell_program,
prepared.login,
process_tree=True,
)
try:
@@ -334,10 +335,10 @@ class ExecTool(Tool):
timeout=prepared.timeout,
)
except asyncio.TimeoutError:
await self._kill_process(process)
await self._kill_process_tree(process)
return ToolResult.error(f"Error: Command timed out after {prepared.timeout} seconds")
except asyncio.CancelledError:
await self._kill_process(process)
await self._kill_process_tree(process)
raise
# Safety-net reap: asyncio *should* have reaped the child via
@@ -374,7 +375,7 @@ class ExecTool(Tool):
# Kill and reap the child if it was spawned but an unexpected
# error prevented communicate() from completing.
if process is not None:
await self._kill_process(process)
await self._kill_process_tree(process)
return ToolResult.error(f"Error executing command: {str(e)}")
async def _execute_session(