From 2792bc53ba3943681499bdf86109007a9f71abdc Mon Sep 17 00:00:00 2001 From: xujunfeng1 <542358203@qq.com> Date: Tue, 21 Jul 2026 10:55:13 +0800 Subject: [PATCH 1/3] Fix shell working directory on Windows --- .../harness/agent/tool/ShellExecuteTool.java | 17 +++++++++++++--- .../agent/tool/ShellExecuteToolTest.java | 20 +++++++++++++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/ShellExecuteTool.java b/agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/ShellExecuteTool.java index f80b8b531..c094869aa 100644 --- a/agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/ShellExecuteTool.java +++ b/agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/ShellExecuteTool.java @@ -20,6 +20,7 @@ import io.agentscope.core.tool.ToolParam; import io.agentscope.harness.agent.filesystem.model.ExecuteResponse; import io.agentscope.harness.agent.filesystem.sandbox.AbstractSandboxFilesystem; +import java.util.Locale; /** * Shell execution tool backed by a {@link AbstractSandboxFilesystem}. @@ -68,7 +69,7 @@ public String execute( return "Error: working_directory must be a relative path within the workspace" + " (absolute paths, '~', and '..' are not allowed)."; } - effectiveCommand = "cd '" + wd.replace("'", "'\\''") + "' && " + command; + effectiveCommand = prefixWorkingDirectory(command, wd, System.getProperty("os.name")); } int timeoutSeconds = timeout != null && timeout > 0 ? timeout : 30; @@ -82,6 +83,16 @@ public String execute( if (result.truncated()) { sb.append("\n(output was truncated)"); } - return sb.toString(); + return sb.toString(); } + + static String prefixWorkingDirectory(String command, String workingDirectory, String osName) { + if (isWindows(osName)) { + return "cd /d \"" + workingDirectory.replace("\"", "\\\"") + "\" && " + command; + } + return "cd '" + workingDirectory.replace("'", "'\\''") + "' && " + command; + } + + private static boolean isWindows(String osName) { + return osName != null && osName.toLowerCase(Locale.ROOT).contains("win"); } -} +} \ No newline at end of file diff --git a/agentscope-harness/src/test/java/io/agentscope/harness/agent/tool/ShellExecuteToolTest.java b/agentscope-harness/src/test/java/io/agentscope/harness/agent/tool/ShellExecuteToolTest.java index 5b69cad17..759c7bf47 100644 --- a/agentscope-harness/src/test/java/io/agentscope/harness/agent/tool/ShellExecuteToolTest.java +++ b/agentscope-harness/src/test/java/io/agentscope/harness/agent/tool/ShellExecuteToolTest.java @@ -15,6 +15,7 @@ */ package io.agentscope.harness.agent.tool; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -65,12 +66,27 @@ void execute_explicitTimeout_isPassedThrough() { @Test void execute_withWorkingDirectory_prefixesCd() { - when(sandbox.execute(eq(RT), eq("cd 'sub' && ls"), eq(30))) + String expectedCommand = ShellExecuteTool.prefixWorkingDirectory("ls", "sub", System.getProperty("os.name")); + when(sandbox.execute(eq(RT), eq(expectedCommand), eq(30))) .thenReturn(new ExecuteResponse("out", 0, false)); String result = tool.execute(RT, "ls", "sub", null); assertTrue(result.contains("Exit code: 0")); - verify(sandbox).execute(RT, "cd 'sub' && ls", 30); + verify(sandbox).execute(RT, expectedCommand, 30); + } + + @Test + void prefixWorkingDirectory_onWindows_usesCmdCompatibleCd() { + assertEquals( + "cd /d \"sub dir\" && dir", + ShellExecuteTool.prefixWorkingDirectory("dir", "sub dir", "Windows 11")); + } + + @Test + void prefixWorkingDirectory_onUnix_keepsSingleQuotedCd() { + assertEquals( + "cd 'it'\\''s' && ls", + ShellExecuteTool.prefixWorkingDirectory("ls", "it's", "Linux")); } } From cfa3fb0ec6c505851e269fb7a9c65f29a62a8080 Mon Sep 17 00:00:00 2001 From: xujunfeng1 <542358203@qq.com> Date: Tue, 21 Jul 2026 10:56:46 +0800 Subject: [PATCH 2/3] Format ShellExecuteTool helper --- .../io/agentscope/harness/agent/tool/ShellExecuteTool.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/ShellExecuteTool.java b/agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/ShellExecuteTool.java index c094869aa..e516671aa 100644 --- a/agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/ShellExecuteTool.java +++ b/agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/ShellExecuteTool.java @@ -83,7 +83,8 @@ public String execute( if (result.truncated()) { sb.append("\n(output was truncated)"); } - return sb.toString(); } + return sb.toString(); + } static String prefixWorkingDirectory(String command, String workingDirectory, String osName) { if (isWindows(osName)) { @@ -95,4 +96,4 @@ static String prefixWorkingDirectory(String command, String workingDirectory, St private static boolean isWindows(String osName) { return osName != null && osName.toLowerCase(Locale.ROOT).contains("win"); } -} \ No newline at end of file +} From 7dd7900246a66ce9a174a6b67fcddb7336a01073 Mon Sep 17 00:00:00 2001 From: xujunfeng1 <542358203@qq.com> Date: Tue, 21 Jul 2026 11:02:28 +0800 Subject: [PATCH 3/3] Apply Spotless formatting --- .../io/agentscope/harness/agent/tool/ShellExecuteToolTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agentscope-harness/src/test/java/io/agentscope/harness/agent/tool/ShellExecuteToolTest.java b/agentscope-harness/src/test/java/io/agentscope/harness/agent/tool/ShellExecuteToolTest.java index 759c7bf47..4e5f1d7a4 100644 --- a/agentscope-harness/src/test/java/io/agentscope/harness/agent/tool/ShellExecuteToolTest.java +++ b/agentscope-harness/src/test/java/io/agentscope/harness/agent/tool/ShellExecuteToolTest.java @@ -66,7 +66,8 @@ void execute_explicitTimeout_isPassedThrough() { @Test void execute_withWorkingDirectory_prefixesCd() { - String expectedCommand = ShellExecuteTool.prefixWorkingDirectory("ls", "sub", System.getProperty("os.name")); + String expectedCommand = + ShellExecuteTool.prefixWorkingDirectory("ls", "sub", System.getProperty("os.name")); when(sandbox.execute(eq(RT), eq(expectedCommand), eq(30))) .thenReturn(new ExecuteResponse("out", 0, false));