diff options
Diffstat (limited to 'packages/core/src/tools')
| -rw-r--r-- | packages/core/src/tools/shell.test.ts | 32 | ||||
| -rw-r--r-- | packages/core/src/tools/shell.ts | 28 |
2 files changed, 27 insertions, 33 deletions
diff --git a/packages/core/src/tools/shell.test.ts b/packages/core/src/tools/shell.test.ts index c0b409fa..79354cf8 100644 --- a/packages/core/src/tools/shell.test.ts +++ b/packages/core/src/tools/shell.test.ts @@ -56,6 +56,7 @@ describe('ShellTool', () => { getSummarizeToolOutputConfig: vi.fn().mockReturnValue(undefined), getWorkspaceContext: () => createMockWorkspaceContext('.'), getGeminiClient: vi.fn(), + getShouldUseNodePtyShell: vi.fn().mockReturnValue(false), } as unknown as Config; shellTool = new ShellTool(mockConfig); @@ -123,13 +124,12 @@ describe('ShellTool', () => { const fullResult: ShellExecutionResult = { rawOutput: Buffer.from(result.output || ''), output: 'Success', - stdout: 'Success', - stderr: '', exitCode: 0, signal: null, error: null, aborted: false, pid: 12345, + executionMethod: 'child_process', ...result, }; resolveExecutionPromise(fullResult); @@ -152,6 +152,9 @@ describe('ShellTool', () => { expect.any(String), expect.any(Function), mockAbortSignal, + false, + undefined, + undefined, ); expect(result.llmContent).toContain('Background PIDs: 54322'); expect(vi.mocked(fs.unlinkSync)).toHaveBeenCalledWith(tmpFile); @@ -164,13 +167,12 @@ describe('ShellTool', () => { resolveShellExecution({ rawOutput: Buffer.from(''), output: '', - stdout: '', - stderr: '', exitCode: 0, signal: null, error: null, aborted: false, pid: 12345, + executionMethod: 'child_process', }); await promise; expect(mockShellExecutionService).toHaveBeenCalledWith( @@ -178,6 +180,9 @@ describe('ShellTool', () => { expect.any(String), expect.any(Function), mockAbortSignal, + false, + undefined, + undefined, ); }); @@ -189,16 +194,14 @@ describe('ShellTool', () => { error, exitCode: 1, output: 'err', - stderr: 'err', rawOutput: Buffer.from('err'), - stdout: '', signal: null, aborted: false, pid: 12345, + executionMethod: 'child_process', }); const result = await promise; - // The final llmContent should contain the user's command, not the wrapper expect(result.llmContent).toContain('Error: wrapped command failed'); expect(result.llmContent).not.toContain('pgrep'); }); @@ -231,13 +234,12 @@ describe('ShellTool', () => { resolveExecutionPromise({ output: 'long output', rawOutput: Buffer.from('long output'), - stdout: 'long output', - stderr: '', exitCode: 0, signal: null, error: null, aborted: false, pid: 12345, + executionMethod: 'child_process', }); const result = await promise; @@ -283,7 +285,6 @@ describe('ShellTool', () => { // First chunk, should be throttled. mockShellOutputCallback({ type: 'data', - stream: 'stdout', chunk: 'hello ', }); expect(updateOutputMock).not.toHaveBeenCalled(); @@ -294,24 +295,22 @@ describe('ShellTool', () => { // Send a second chunk. THIS event triggers the update with the CUMULATIVE content. mockShellOutputCallback({ type: 'data', - stream: 'stderr', - chunk: 'world', + chunk: 'hello world', }); // It should have been called once now with the combined output. expect(updateOutputMock).toHaveBeenCalledOnce(); - expect(updateOutputMock).toHaveBeenCalledWith('hello \nworld'); + expect(updateOutputMock).toHaveBeenCalledWith('hello world'); resolveExecutionPromise({ rawOutput: Buffer.from(''), output: '', - stdout: '', - stderr: '', exitCode: 0, signal: null, error: null, aborted: false, pid: 12345, + executionMethod: 'child_process', }); await promise; }); @@ -350,13 +349,12 @@ describe('ShellTool', () => { resolveExecutionPromise({ rawOutput: Buffer.from(''), output: '', - stdout: '', - stderr: '', exitCode: 0, signal: null, error: null, aborted: false, pid: 12345, + executionMethod: 'child_process', }); await promise; }); diff --git a/packages/core/src/tools/shell.ts b/packages/core/src/tools/shell.ts index 8d5c624c..3fce7c2d 100644 --- a/packages/core/src/tools/shell.ts +++ b/packages/core/src/tools/shell.ts @@ -96,6 +96,8 @@ class ShellToolInvocation extends BaseToolInvocation< async execute( signal: AbortSignal, updateOutput?: (output: string) => void, + terminalColumns?: number, + terminalRows?: number, ): Promise<ToolResult> { const strippedCommand = stripShellWrapper(this.params.command); @@ -128,13 +130,11 @@ class ShellToolInvocation extends BaseToolInvocation< this.params.directory || '', ); - let cumulativeStdout = ''; - let cumulativeStderr = ''; - + let cumulativeOutput = ''; let lastUpdateTime = Date.now(); let isBinaryStream = false; - const { result: resultPromise } = ShellExecutionService.execute( + const { result: resultPromise } = await ShellExecutionService.execute( commandToExecute, cwd, (event: ShellOutputEvent) => { @@ -147,15 +147,9 @@ class ShellToolInvocation extends BaseToolInvocation< switch (event.type) { case 'data': - if (isBinaryStream) break; // Don't process text if we are in binary mode - if (event.stream === 'stdout') { - cumulativeStdout += event.chunk; - } else { - cumulativeStderr += event.chunk; - } - currentDisplayOutput = - cumulativeStdout + - (cumulativeStderr ? `\n${cumulativeStderr}` : ''); + if (isBinaryStream) break; + cumulativeOutput = event.chunk; + currentDisplayOutput = cumulativeOutput; if (Date.now() - lastUpdateTime > OUTPUT_UPDATE_INTERVAL_MS) { shouldUpdate = true; } @@ -186,6 +180,9 @@ class ShellToolInvocation extends BaseToolInvocation< } }, signal, + this.config.getShouldUseNodePtyShell(), + terminalColumns, + terminalRows, ); const result = await resultPromise; @@ -217,7 +214,7 @@ class ShellToolInvocation extends BaseToolInvocation< if (result.aborted) { llmContent = 'Command was cancelled by user before it could complete.'; if (result.output.trim()) { - llmContent += ` Below is the output (on stdout and stderr) before it was cancelled:\n${result.output}`; + llmContent += ` Below is the output before it was cancelled:\n${result.output}`; } else { llmContent += ' There was no output before it was cancelled.'; } @@ -231,8 +228,7 @@ class ShellToolInvocation extends BaseToolInvocation< llmContent = [ `Command: ${this.params.command}`, `Directory: ${this.params.directory || '(root)'}`, - `Stdout: ${result.stdout || '(empty)'}`, - `Stderr: ${result.stderr || '(empty)'}`, + `Output: ${result.output || '(empty)'}`, `Error: ${finalError}`, // Use the cleaned error string. `Exit Code: ${result.exitCode ?? '(none)'}`, `Signal: ${result.signal ?? '(none)'}`, |
