@@ -150,6 +150,74 @@ function isModelNotSupportedError(output) {
150150 return MODEL_NOT_SUPPORTED_PATTERN . test ( output ) ;
151151}
152152
153+ /**
154+ * Determine whether the current run phase is threat detection.
155+ * @param {string | undefined | null } phase
156+ * @returns {boolean }
157+ */
158+ function isDetectionPhase ( phase ) {
159+ return (
160+ String ( phase || "" )
161+ . trim ( )
162+ . toLowerCase ( ) === "detection"
163+ ) ;
164+ }
165+
166+ /**
167+ * Check whether a model is present in AWF /reflect endpoint data.
168+ * @param {string } model
169+ * @param {unknown } reflectData
170+ * @returns {boolean }
171+ */
172+ function isModelAvailableInReflectData ( model , reflectData ) {
173+ const normalizedModel = typeof model === "string" ? model . trim ( ) : "" ;
174+ if ( ! normalizedModel ) return false ;
175+ if ( ! reflectData || typeof reflectData !== "object" ) return false ;
176+
177+ // TypeScript needs explicit 'in' check or cast before property access on narrowed object type
178+ const endpoints = 'endpoints' in reflectData && Array . isArray ( reflectData . endpoints ) ? reflectData . endpoints : [ ] ;
179+ for ( const endpoint of endpoints ) {
180+ if ( ! endpoint || endpoint . configured !== true || ! Array . isArray ( endpoint . models ) ) {
181+ continue ;
182+ }
183+ if ( endpoint . models . includes ( normalizedModel ) ) {
184+ return true ;
185+ }
186+ }
187+ return false ;
188+ }
189+
190+ /**
191+ * Load saved AWF /reflect data and check whether a model is present.
192+ * @param {string } model
193+ * @param {{
194+ * reflectPath?: string,
195+ * readFileSync?: (path: string, encoding: string) => string,
196+ * logger?: (msg: string) => void,
197+ * }} [options]
198+ * @returns {boolean }
199+ */
200+ function isModelAvailableInReflectFile ( model , options ) {
201+ const normalizedModel = typeof model === "string" ? model . trim ( ) : "" ;
202+ const reflectPath = ( options && options . reflectPath ) || AWF_REFLECT_OUTPUT_PATH ;
203+ const readFile = ( options && options . readFileSync ) || fs . readFileSync ;
204+ const logger = ( options && options . logger ) || log ;
205+ if ( ! normalizedModel ) {
206+ logger ( "awf-reflect: model availability check skipped (model is empty)" ) ;
207+ return false ;
208+ }
209+
210+ try {
211+ const raw = readFile ( reflectPath , "utf8" ) ;
212+ const reflectData = JSON . parse ( raw ) ;
213+ return isModelAvailableInReflectData ( normalizedModel , reflectData ) ;
214+ } catch ( error ) {
215+ const err = /** @type {Error } */ error ;
216+ logger ( `awf-reflect: unable to read model availability from ${ reflectPath } : ${ err . message } ` ) ;
217+ return false ;
218+ }
219+ }
220+
153221/**
154222 * Determines if the collected output contains a "No authentication information found" error.
155223 * This means no auth token (COPILOT_GITHUB_TOKEN, GH_TOKEN, or GITHUB_TOKEN) is available
@@ -470,6 +538,7 @@ async function main() {
470538 let scheduledExit2Retries = 0 ;
471539 let scheduledExit2RetryAttempted = false ;
472540 let useContinueOnRetry = false ;
541+ let modelNotSupportedReflectRetryAttempted = false ;
473542 // Once set to true, --continue is never re-enabled for the remainder of this run.
474543 // This prevents a broken --continue recovery from resurrecting --continue on the next attempt.
475544 let continueDisabledPermanently = false ;
@@ -563,6 +632,19 @@ async function main() {
563632
564633 // Model-not-supported errors are persistent — retrying will not help.
565634 if ( isModelNotSupported ) {
635+ if ( ! modelNotSupportedReflectRetryAttempted && attempt < MAX_RETRIES && isDetectionPhase ( process . env . GH_AW_PHASE ) && process . env . AWF_REFLECT_ENABLED === "1" ) {
636+ const configuredModel = process . env . COPILOT_MODEL || "" ;
637+ modelNotSupportedReflectRetryAttempted = true ;
638+ log ( `attempt ${ attempt + 1 } : model not supported during detection — refreshing awf-reflect to rule out startup registry race` ) ;
639+ await fetchAWFReflect ( { logger : log } ) ;
640+ if ( isModelAvailableInReflectFile ( configuredModel , { logger : log } ) ) {
641+ useContinueOnRetry = false ;
642+ continueDisabledPermanently = true ;
643+ log ( `attempt ${ attempt + 1 } : refreshed awf-reflect now includes model '${ configuredModel } ' — retrying once as fresh run` ) ;
644+ continue ;
645+ }
646+ log ( `attempt ${ attempt + 1 } : refreshed awf-reflect does not include model '${ configuredModel || "(none)" } ' — treating as non-retryable` ) ;
647+ }
566648 log ( `attempt ${ attempt + 1 } : model not supported — not retrying (the requested model is unavailable for this subscription tier; specify a supported model in the workflow frontmatter)` ) ;
567649 break ;
568650 }
@@ -662,6 +744,9 @@ if (typeof module !== "undefined" && module.exports) {
662744 extractDeniedCommands,
663745 fetchAWFReflect,
664746 fetchModelsFromUrl,
747+ isDetectionPhase,
748+ isModelAvailableInReflectData,
749+ isModelAvailableInReflectFile,
665750 countPermissionDeniedIssues,
666751 detectCopilotErrors,
667752 hasNumerousPermissionDeniedIssues,
0 commit comments