55import importlib .util
66import json
77import re
8+ import subprocess
89import sys
10+ import time
911import tomllib
1012from pathlib import Path
1113
2224KERNEL_BROWSER_SCRIPT = (
2325 REPO_ROOT / "src" / "clawbench" / "runtime" / "harbor" / "kernel-browser.py"
2426)
27+ HARBOR_AGENT_WRAPPER = (
28+ REPO_ROOT / "src" / "clawbench" / "runtime" / "harbor" / "wrap-harbor-agent.sh"
29+ )
2530
2631
2732def _task () -> dict :
@@ -114,6 +119,7 @@ def test_kernel_setup_and_test_scripts_wire_lifecycle(
114119 assert "export CLAWBENCH_BROWSER_CDP_URL_FILE=" in setup
115120 assert "trap cleanup_browser EXIT" in setup
116121 assert "kernel-browser.py cleanup" in setup
122+ assert "wrap-harbor-agent.sh" in setup
117123 assert "127.0.0.1:7878/json/version" in setup
118124
119125 test = (tests / "test.sh" ).read_text ()
@@ -124,6 +130,40 @@ def test_kernel_setup_and_test_scripts_wire_lifecycle(
124130 assert (env_dir / "harbor" / "browser_runtime_providers.py" ).is_file ()
125131
126132
133+ def test_stop_wrapper_interrupts_agent_and_exits_cleanly (tmp_path : Path ) -> None :
134+ bin_dir = tmp_path / "bin"
135+ bin_dir .mkdir ()
136+ fake_agent = bin_dir / "claude"
137+ started = tmp_path / "started"
138+ stop_file = tmp_path / "stop-requested"
139+ stop_result = tmp_path / "agent-stop.json"
140+ fake_agent .write_text (
141+ "#!/bin/bash\n "
142+ "trap 'exit 130' INT\n "
143+ 'touch "$FAKE_AGENT_STARTED"\n '
144+ "while true; do sleep 0.1; done\n "
145+ )
146+ fake_agent .chmod (0o755 )
147+ env = {
148+ "PATH" : f"{ bin_dir } :/usr/bin:/bin" ,
149+ "HOME" : str (tmp_path ),
150+ "FAKE_AGENT_STARTED" : str (started ),
151+ "CLAWBENCH_STOP_FILE" : str (stop_file ),
152+ "CLAWBENCH_STOP_RESULT" : str (stop_result ),
153+ }
154+
155+ subprocess .run ([HARBOR_AGENT_WRAPPER , "claude" ], env = env , check = True )
156+ process = subprocess .Popen ([fake_agent ], env = env )
157+ deadline = time .monotonic () + 2
158+ while not started .exists () and time .monotonic () < deadline :
159+ time .sleep (0.01 )
160+ assert started .exists ()
161+
162+ stop_file .touch ()
163+ assert process .wait (timeout = 3 ) == 0
164+ assert json .loads (stop_result .read_text ())["signal" ] == "INT"
165+
166+
127167def test_local_runtime_default_has_no_kernel_hooks (tmp_path : Path ) -> None :
128168 case = tmp_path / "case"
129169 case .mkdir ()
0 commit comments