diff --git a/packages/core/src/tools/shell.test.ts b/packages/core/src/tools/shell.test.ts index b431f494..b98e7158 100644 --- a/packages/core/src/tools/shell.test.ts +++ b/packages/core/src/tools/shell.test.ts @@ -833,6 +833,41 @@ describe('ShellTool', () => { }); describe('Windows background execution', () => { + it('should append keep-alive ping with && on Windows for background tasks', async () => { + vi.mocked(os.platform).mockReturnValue('win32'); + const mockAbortSignal = new AbortController().signal; + + const invocation = shellTool.build({ + command: 'npm start', + is_background: true, + }); + + const promise = invocation.execute(mockAbortSignal); + + // Simulate immediate success (process started) + resolveExecutionPromise({ + rawOutput: Buffer.from(''), + output: '', + exitCode: 0, + signal: null, + error: null, + aborted: false, + pid: 12345, + executionMethod: 'child_process', + }); + + await promise; + + expect(mockShellExecutionService).toHaveBeenCalledWith( + expect.stringContaining('npm start && ping -n 86400 127.0.0.1 >nul'), + expect.any(String), + expect.any(Function), + expect.any(AbortSignal), + false, + {}, + ); + }); + it('should detect immediate failure in Windows background task', async () => { vi.mocked(os.platform).mockReturnValue('win32'); const mockAbortSignal = new AbortController().signal; diff --git a/packages/core/src/tools/shell.ts b/packages/core/src/tools/shell.ts index 55bc4df0..8d7610d4 100644 --- a/packages/core/src/tools/shell.ts +++ b/packages/core/src/tools/shell.ts @@ -174,7 +174,7 @@ export class ShellToolInvocation extends BaseToolInvocation< while (cmd.endsWith('&')) { cmd = cmd.slice(0, -1).trim(); } - finalCommand = cmd + ' & ping -n 86400 127.0.0.1 >nul'; + finalCommand = cmd + ' && ping -n 86400 127.0.0.1 >nul'; } // pgrep is not available on Windows, so we can't get background PIDs