Skip to content

Commit e3e4b23

Browse files
CopilotTyriar
andcommitted
Improve prompt detection heuristics and add comprehensive tests
Co-authored-by: Tyriar <[email protected]>
1 parent 6e6bc29 commit e3e4b23

File tree

2 files changed

+83
-4
lines changed

2 files changed

+83
-4
lines changed

src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/executeStrategy.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ export async function waitForIdle(onData: Event<unknown>, idleDurationMs: number
3939
/**
4040
* Detects if the given text content appears to end with a common prompt pattern.
4141
* This is used as a heuristic to determine if a command has finished executing.
42+
*
43+
* The idea is to do basic regex checks after the initial timeout to extend the
44+
* timeout if it doesn't look like a common prompt format, giving commands more
45+
* time to complete before assuming they're done.
4246
*/
4347
export function detectsCommonPromptPattern(content: string): boolean {
4448
if (!content || content.trim().length === 0) {
@@ -102,6 +106,9 @@ export function detectsCommonPromptPattern(content: string): boolean {
102106
* Enhanced version of waitForIdle that uses prompt detection heuristics.
103107
* After the initial timeout, checks if the terminal content looks like a common prompt.
104108
* If not, extends the timeout to give the command more time to complete.
109+
*
110+
* This addresses the need for better heuristics around prompt characters for evaluating
111+
* no shell integration command finished state, as requested in the issue.
105112
*/
106113
export async function waitForIdleWithPromptHeuristics(
107114
onData: Event<unknown>,
@@ -118,12 +125,13 @@ export async function waitForIdleWithPromptHeuristics(
118125
if (xterm) {
119126
// Get the current visible content from the terminal
120127
const buffer = xterm.raw.buffer.active;
121-
const viewportHeight = xterm.raw.rows;
122128
let content = '';
123129

124-
// Read the last few lines of the terminal to detect prompt patterns
125-
const startLine = Math.max(0, buffer.baseY + buffer.cursorY - viewportHeight + 1);
126-
const endLine = buffer.baseY + buffer.cursorY + 1;
130+
// Read the current line and a few lines above to detect prompt patterns
131+
// Focus on the area around the cursor, which is most likely to contain the prompt
132+
const currentLine = buffer.baseY + buffer.cursorY;
133+
const startLine = Math.max(0, currentLine - 2); // Read up to 2 lines above
134+
const endLine = Math.min(buffer.length, currentLine + 1); // Read current line
127135

128136
for (let i = startLine; i < endLine; i++) {
129137
const line = buffer.getLine(i);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { strictEqual } from 'assert';
7+
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js';
8+
import { detectsCommonPromptPattern } from '../../browser/executeStrategy/executeStrategy.js';
9+
10+
suite('Execute Strategy - Prompt Detection', () => {
11+
ensureNoDisposablesAreLeakedInTestSuite();
12+
13+
test('detectsCommonPromptPattern should detect PowerShell prompts', () => {
14+
strictEqual(detectsCommonPromptPattern('PS C:\\>'), true);
15+
strictEqual(detectsCommonPromptPattern('PS C:\\Windows\\System32>'), true);
16+
strictEqual(detectsCommonPromptPattern('PS C:\\Users\\test> '), true);
17+
});
18+
19+
test('detectsCommonPromptPattern should detect Command Prompt', () => {
20+
strictEqual(detectsCommonPromptPattern('C:\\>'), true);
21+
strictEqual(detectsCommonPromptPattern('C:\\Windows\\System32>'), true);
22+
strictEqual(detectsCommonPromptPattern('D:\\test> '), true);
23+
});
24+
25+
test('detectsCommonPromptPattern should detect Bash prompts', () => {
26+
strictEqual(detectsCommonPromptPattern('user@host:~$ '), true);
27+
strictEqual(detectsCommonPromptPattern('$ '), true);
28+
strictEqual(detectsCommonPromptPattern('[user@host ~]$ '), true);
29+
});
30+
31+
test('detectsCommonPromptPattern should detect root prompts', () => {
32+
strictEqual(detectsCommonPromptPattern('root@host:~# '), true);
33+
strictEqual(detectsCommonPromptPattern('# '), true);
34+
strictEqual(detectsCommonPromptPattern('[root@host ~]# '), true);
35+
});
36+
37+
test('detectsCommonPromptPattern should detect Python REPL', () => {
38+
strictEqual(detectsCommonPromptPattern('>>> '), true);
39+
strictEqual(detectsCommonPromptPattern('>>>'), true);
40+
});
41+
42+
test('detectsCommonPromptPattern should detect starship prompts', () => {
43+
strictEqual(detectsCommonPromptPattern('~ ❯ '), true);
44+
strictEqual(detectsCommonPromptPattern('/path/to/project ❯'), true);
45+
});
46+
47+
test('detectsCommonPromptPattern should detect generic prompts', () => {
48+
strictEqual(detectsCommonPromptPattern('test> '), true);
49+
strictEqual(detectsCommonPromptPattern('someprompt% '), true);
50+
});
51+
52+
test('detectsCommonPromptPattern should handle multiline content', () => {
53+
const multilineContent = `command output line 1
54+
command output line 2
55+
user@host:~$ `;
56+
strictEqual(detectsCommonPromptPattern(multilineContent), true);
57+
});
58+
59+
test('detectsCommonPromptPattern should reject non-prompt content', () => {
60+
strictEqual(detectsCommonPromptPattern('just some output'), false);
61+
strictEqual(detectsCommonPromptPattern('error: command not found'), false);
62+
strictEqual(detectsCommonPromptPattern(''), false);
63+
strictEqual(detectsCommonPromptPattern(' '), false);
64+
});
65+
66+
test('detectsCommonPromptPattern should handle edge cases', () => {
67+
strictEqual(detectsCommonPromptPattern('output\n\n\n'), false);
68+
strictEqual(detectsCommonPromptPattern('\n\n$ \n\n'), true); // prompt with surrounding whitespace
69+
strictEqual(detectsCommonPromptPattern('output\nPS C:\\> '), true); // prompt at end after output
70+
});
71+
});

0 commit comments

Comments
 (0)