diff --git a/strix/interface/tui/app.py b/strix/interface/tui/app.py index 0987720..2b8f89b 100644 --- a/strix/interface/tui/app.py +++ b/strix/interface/tui/app.py @@ -751,7 +751,7 @@ class StrixTUIApp(App): # type: ignore[misc] self.report_state.cleanup() def signal_handler(_signum: int, _frame: Any) -> None: - self._teardown_sandbox_blocking(timeout=10.0) + self._fire_sandbox_cleanup() self.report_state.cleanup(status="interrupted") sys.exit(0) @@ -1705,36 +1705,24 @@ class StrixTUIApp(App): # type: ignore[misc] ) async def action_custom_quit(self) -> None: - await asyncio.to_thread(self._teardown_sandbox_blocking, timeout=10.0) + self._fire_sandbox_cleanup() if self._scan_thread and self._scan_thread.is_alive(): self._scan_stop_event.set() - self._scan_thread.join(timeout=2.0) self.report_state.cleanup() self.exit() - def _teardown_sandbox_blocking(self, *, timeout: float) -> None: + def _fire_sandbox_cleanup(self) -> None: loop = self._scan_loop if loop is None or loop.is_closed(): return run_name = self.scan_config.get("run_name") if not run_name: return - future = asyncio.run_coroutine_threadsafe( - session_manager.cleanup(run_name), - loop, - ) - try: - future.result(timeout=timeout) - except TimeoutError: - logger.warning( - "Sandbox cleanup timed out after %.1fs; container may still be running", - timeout, - ) - except Exception: - logger.exception("Sandbox cleanup failed") + with contextlib.suppress(Exception): + asyncio.run_coroutine_threadsafe(session_manager.cleanup(run_name), loop) def _is_widget_safe(self, widget: Any) -> bool: try: diff --git a/strix/runtime/docker_client.py b/strix/runtime/docker_client.py index fb6f680..2a75383 100644 --- a/strix/runtime/docker_client.py +++ b/strix/runtime/docker_client.py @@ -22,6 +22,7 @@ re-merging the parent body. Track upstream for an injection hook. from __future__ import annotations +import contextlib import logging import uuid from typing import Any @@ -34,6 +35,8 @@ from agents.sandbox.sandboxes.docker import ( _manifest_requires_fuse, _manifest_requires_sys_admin, ) +from agents.sandbox.session.sandbox_session import SandboxSession +from docker import errors as docker_errors # type: ignore[import-untyped, unused-ignore] from docker.models.containers import Container # type: ignore[import-untyped, unused-ignore] from docker.utils import parse_repository_tag # type: ignore[import-untyped, unused-ignore] @@ -121,3 +124,10 @@ class StrixDockerSandboxClient(DockerSandboxClient): image, ) return container + + async def delete(self, session: SandboxSession) -> SandboxSession: + container_id = getattr(getattr(session._inner, "state", None), "container_id", None) + if container_id: + with contextlib.suppress(docker_errors.NotFound, docker_errors.APIError): + self.docker_client.containers.get(container_id).kill() + return await super().delete(session)