@@ -14,6 +14,9 @@ import { IChatService } from '../../../chat/common/chatService.js';
1414import { ChatMessageRole , ILanguageModelsService } from '../../../chat/common/languageModels.js' ;
1515import { IToolInvocationContext } from '../../../chat/common/languageModelToolsService.js' ;
1616import type { Terminal as RawXtermTerminal , IMarker as IXtermMarker } from '@xterm/xterm' ;
17+ import { Task } from '../../../tasks/common/taskService.js' ;
18+ import { IMarkerData , IMarkerService } from '../../../../../platform/markers/common/markers.js' ;
19+ import { ProblemMatcher , ProblemMatcherRegistry } from '../../../tasks/common/problemMatcher.js' ;
1720
1821export const enum PollingConsts {
1922 MinNoDataEvents = 2 , // Minimum number of no data checks before considering the terminal idle
@@ -34,7 +37,8 @@ export async function racePollingOrPrompt(
3437 originalResult : { terminalExecutionIdleBeforeTimeout : boolean ; output : string ; pollDurationMs ?: number ; modelOutputEvalResponse ?: string } ,
3538 token : CancellationToken ,
3639 languageModelsService : ILanguageModelsService ,
37- execution : { getOutput : ( ) => string ; isActive ?: ( ) => Promise < boolean > }
40+ markerService : IMarkerService ,
41+ execution : { getOutput : ( ) => string ; isActive ?: ( ) => Promise < boolean > ; task ?: Task ; beginsPattern ?: string ; endsPattern ?: string ; dependencyTasks ?: Task [ ] }
3842) : Promise < { terminalExecutionIdleBeforeTimeout : boolean ; output : string ; pollDurationMs ?: number ; modelOutputEvalResponse ?: string } > {
3943 const pollPromise = pollFn ( ) ;
4044 const { promise : promptPromise , part } = promptFn ( ) ;
@@ -63,7 +67,7 @@ export async function racePollingOrPrompt(
6367 const promptResult = raceResult . result as boolean ;
6468 if ( promptResult ) {
6569 // User accepted, poll again (extended)
66- return await pollForOutputAndIdle ( execution , true , token , languageModelsService ) ;
70+ return await pollForOutputAndIdle ( execution , true , token , languageModelsService , markerService ) ;
6771 } else {
6872 return originalResult ; // User rejected, return the original result
6973 }
@@ -95,10 +99,12 @@ export function getOutput(terminal?: Pick<RawXtermTerminal, 'buffer'>, startMark
9599}
96100
97101export async function pollForOutputAndIdle (
98- execution : { getOutput : ( ) => string ; isActive ?: ( ) => Promise < boolean > } ,
102+ execution : { getOutput : ( ) => string ; isActive ?: ( ) => Promise < boolean > ; task ?: Pick < Task , 'configurationProperties' > ; dependencyTasks ?: Task [ ] } ,
99103 extendedPolling : boolean ,
100104 token : CancellationToken ,
101- languageModelsService : ILanguageModelsService ,
105+ languageModelsService : Pick < ILanguageModelsService , 'selectLanguageModels' | 'sendChatRequest' > ,
106+ markerService : Pick < IMarkerService , 'read' > ,
107+ knownMatchers ?: ProblemMatcher [ ]
102108) : Promise < { terminalExecutionIdleBeforeTimeout : boolean ; output : string ; pollDurationMs ?: number ; modelOutputEvalResponse ?: string } > {
103109 const maxWaitMs = extendedPolling ? PollingConsts . ExtendedPollingMaxDuration : PollingConsts . FirstPollingMaxDuration ;
104110 const maxInterval = PollingConsts . MaxPollingIntervalDuration ;
@@ -149,10 +155,32 @@ export async function pollForOutputAndIdle(
149155 lastBufferLength = currentBufferLength ;
150156 continue ;
151157 }
152- terminalExecutionIdleBeforeTimeout = true ;
153- const modelOutputEvalResponse = await assessOutputForErrors ( buffer , token , languageModelsService ) ;
154- return { modelOutputEvalResponse, terminalExecutionIdleBeforeTimeout, output : buffer , pollDurationMs : Date . now ( ) - pollStartTime + ( extendedPolling ? PollingConsts . FirstPollingMaxDuration : 0 ) } ;
155158 }
159+ terminalExecutionIdleBeforeTimeout = true ;
160+ if ( execution . task ) {
161+ const problems = await getProblemsForTasks ( execution . task , markerService , execution . dependencyTasks , knownMatchers ) ;
162+ if ( problems ) {
163+ // Problem matchers exist for this task
164+ const problemList : string [ ] = [ ] ;
165+ for ( const [ , problemArray ] of problems . entries ( ) ) {
166+ if ( problemArray . length ) {
167+ for ( const p of problemArray ) {
168+ problemList . push ( `${ p . severity } : ${ p . message } ` ) ;
169+ }
170+ }
171+ }
172+ if ( problemList . length === 0 ) {
173+ return { terminalExecutionIdleBeforeTimeout, output : 'The task succeeded with no problems.' , pollDurationMs : Date . now ( ) - pollStartTime + ( extendedPolling ? PollingConsts . FirstPollingMaxDuration : 0 ) } ;
174+ }
175+ return {
176+ terminalExecutionIdleBeforeTimeout,
177+ output : problemList . join ( '\n' ) ,
178+ pollDurationMs : Date . now ( ) - pollStartTime + ( extendedPolling ? PollingConsts . FirstPollingMaxDuration : 0 )
179+ } ;
180+ }
181+ }
182+ const modelOutputEvalResponse = await assessOutputForErrors ( buffer , token , languageModelsService ) ;
183+ return { modelOutputEvalResponse, terminalExecutionIdleBeforeTimeout, output : buffer , pollDurationMs : Date . now ( ) - pollStartTime + ( extendedPolling ? PollingConsts . FirstPollingMaxDuration : 0 ) } ;
156184 }
157185 return { terminalExecutionIdleBeforeTimeout : false , output : buffer , pollDurationMs : Date . now ( ) - pollStartTime + ( extendedPolling ? PollingConsts . FirstPollingMaxDuration : 0 ) } ;
158186}
@@ -168,7 +196,7 @@ export function promptForMorePolling(command: string, token: CancellationToken,
168196 let part : ChatElicitationRequestPart | undefined = undefined ;
169197 const promise = new Promise < boolean > ( resolve => {
170198 const thePart = part = new ChatElicitationRequestPart (
171- new MarkdownString ( localize ( 'poll.terminal.waiting' , "Continue waiting?" ) ) ,
199+ new MarkdownString ( localize ( 'poll.terminal.waiting' , "Continue waiting for \`{0}\`?" , command ) ) ,
172200 new MarkdownString ( localize ( 'poll.terminal.polling' , "This will continue to poll for output to determine when the terminal becomes idle for up to 2 minutes." ) ) ,
173201 '' ,
174202 localize ( 'poll.terminal.accept' , 'Yes' ) ,
@@ -192,7 +220,7 @@ export function promptForMorePolling(command: string, token: CancellationToken,
192220 return { promise : Promise . resolve ( false ) } ;
193221}
194222
195- export async function assessOutputForErrors ( buffer : string , token : CancellationToken , languageModelsService : ILanguageModelsService ) : Promise < string > {
223+ export async function assessOutputForErrors ( buffer : string , token : CancellationToken , languageModelsService : Pick < ILanguageModelsService , 'selectLanguageModels' | 'sendChatRequest' > ) : Promise < string > {
196224 const models = await languageModelsService . selectLanguageModels ( { vendor : 'copilot' , family : 'gpt-4o-mini' } ) ;
197225 if ( ! models . length ) {
198226 return 'No models available' ;
@@ -223,3 +251,37 @@ export async function assessOutputForErrors(buffer: string, token: CancellationT
223251 return 'Error occurred ' + err ;
224252 }
225253}
254+
255+ export function getProblemsForTasks ( task : Pick < Task , 'configurationProperties' > , markerService : Pick < IMarkerService , 'read' > , dependencyTasks ?: Task [ ] , knownMatchers ?: ProblemMatcher [ ] ) : Map < string , IMarkerData [ ] > | undefined {
256+ const problemsMap = new Map < string , IMarkerData [ ] > ( ) ;
257+ let hadDefinedMatcher = false ;
258+
259+ const collectProblems = ( t : Pick < Task , 'configurationProperties' > ) => {
260+ const matchers = Array . isArray ( t . configurationProperties . problemMatchers )
261+ ? t . configurationProperties . problemMatchers
262+ : ( t . configurationProperties . problemMatchers ? [ t . configurationProperties . problemMatchers ] : [ ] ) ;
263+ for ( const matcherRef of matchers ) {
264+ const matcher = typeof matcherRef === 'string'
265+ ? ProblemMatcherRegistry . get ( matcherRef ) ?? knownMatchers ?. find ( m => m . owner === matcherRef )
266+ : matcherRef ;
267+ if ( matcher ?. owner ) {
268+ const markers = markerService . read ( { owner : matcher . owner } ) ;
269+ hadDefinedMatcher = true ;
270+ if ( markers . length ) {
271+ problemsMap . set ( matcher . owner , markers ) ;
272+ }
273+ }
274+ }
275+ } ;
276+
277+ collectProblems ( task ) ;
278+
279+ if ( problemsMap . size === 0 && dependencyTasks ) {
280+ for ( const depTask of dependencyTasks ) {
281+ collectProblems ( depTask ) ;
282+ }
283+ }
284+
285+ return hadDefinedMatcher ? problemsMap : undefined ;
286+ }
287+
0 commit comments