diff --git a/tests/gateway/test_api_server_active_work_drain.py b/tests/gateway/test_api_server_active_work_drain.py index 5c3e03c9a9..0330a5d212 100644 --- a/tests/gateway/test_api_server_active_work_drain.py +++ b/tests/gateway/test_api_server_active_work_drain.py @@ -620,7 +620,11 @@ class TestShutdownSettleWindow: settled_at_kill: list = [] - def _spy_kill_all(task_id=None): + def _spy_kill_all(task_id=None, **kwargs): + # kwargs carry kill_all's keyword-only args; the shutdown sweep + # passes source="gateway_shutdown" (#41225) so persisted jobs are + # still killed on host exit. + assert kwargs.get("source") == "gateway_shutdown", kwargs settled_at_kill.append(api.settled) return 0 diff --git a/tests/gateway/test_gateway_shutdown.py b/tests/gateway/test_gateway_shutdown.py index b0978fa641..1a4dbdceee 100644 --- a/tests/gateway/test_gateway_shutdown.py +++ b/tests/gateway/test_gateway_shutdown.py @@ -216,7 +216,11 @@ async def test_gateway_stop_kills_tool_subprocesses_before_adapter_disconnect_on call_order: list[str] = [] - def _fake_kill_all(task_id=None): + def _fake_kill_all(task_id=None, **kwargs): + # kwargs carry kill_all's keyword-only args (source, consume_output); + # the shutdown sweep passes source="gateway_shutdown" so a + # persist_on_release job (#41225) is still killed on host exit. + assert kwargs.get("source") == "gateway_shutdown", kwargs call_order.append("kill_all") return 2 diff --git a/tests/tools/test_code_execution.py b/tests/tools/test_code_execution.py index fa69d4c332..d7c6a6516c 100644 --- a/tests/tools/test_code_execution.py +++ b/tests/tools/test_code_execution.py @@ -387,7 +387,7 @@ class TestStubSchemaDrift(unittest.TestCase): # Parameters that are internal (injected by the handler, not user-facing) _INTERNAL_PARAMS = {"task_id", "user_task"} # Parameters intentionally blocked in the sandbox - _BLOCKED_TERMINAL_PARAMS = {"background", "pty", "notify", "notify_on_complete", "watch_patterns", "heartbeat"} + _BLOCKED_TERMINAL_PARAMS = {"background", "pty", "notify", "notify_on_complete", "watch_patterns", "heartbeat", "persist_on_release"} def test_stubs_cover_all_schema_params(self): """Every user-facing parameter in the real schema must appear in the diff --git a/tests/tools/test_code_execution_file_rpc.py b/tests/tools/test_code_execution_file_rpc.py index 4452660bf0..6eea798fec 100644 --- a/tests/tools/test_code_execution_file_rpc.py +++ b/tests/tools/test_code_execution_file_rpc.py @@ -82,7 +82,7 @@ def test_generated_file_rpc_kwargs_correlation_and_authority(tmp_path, monkeypat assert not (rpc / f"res_{seq:06d}").exists() assert seen == [] and counter == [0] for name, args in CALLS.items(): - blocked = {"background", "heartbeat", "pty", "notify", "notify_on_complete", "watch_patterns"} if name == "terminal" else set() + blocked = {"background", "heartbeat", "pty", "notify", "notify_on_complete", "watch_patterns", "persist_on_release"} if name == "terminal" else set() schema_keys = set(registry.get_entry(name).schema["parameters"]["properties"]) - blocked assert schema_keys <= set(args), (name, schema_keys - set(args)) assert namespace[name](**args) == {"name": name, "args": args} diff --git a/tools/code_execution_rpc.py b/tools/code_execution_rpc.py index c7bc1e9f3f..ea784c63f6 100644 --- a/tools/code_execution_rpc.py +++ b/tools/code_execution_rpc.py @@ -22,7 +22,7 @@ from tools.registry import tool_error logger = logging.getLogger("tools.code_execution_tool") # Terminal parameters that must not be used from ephemeral sandbox scripts. -_TERMINAL_BLOCKED_PARAMS = {"background", "pty", "notify", "notify_on_complete", "watch_patterns", "heartbeat"} +_TERMINAL_BLOCKED_PARAMS = {"background", "pty", "notify", "notify_on_complete", "watch_patterns", "heartbeat", "persist_on_release"} def _default_dispatch(task_id):