@@ -72,34 +72,86 @@ function getAgentName(assignee) {
7272
7373/**
7474 * Return list of coding agent bot login names that are currently available as assignable actors
75- * in this repository, as determined by checkUserCanBeAssigned.
75+ * in this repository, preferring issue-scoped checks when issue/PR context is available
76+ * and falling back to repository-scoped checks.
7677 * @param {string } owner
7778 * @param {string } repo
79+ * @param {number|string|null } [issueNumber]
7880 * @param {Object } [githubClient] - Authenticated GitHub client (defaults to global github)
7981 * @returns {Promise<string[]> }
8082 */
81- async function getAvailableAgentLogins ( owner , repo , githubClient = github ) {
83+ async function getAvailableAgentLogins ( owner , repo , issueNumber = null , githubClient = github ) {
8284 // Deduplicate defensively so future alias additions across agents do not duplicate REST lookups.
8385 const knownValues = [ ...new Set ( Object . values ( AGENT_LOGIN_NAMES ) . flat ( ) ) ] ;
8486 const available = [ ] ;
8587 for ( const login of knownValues ) {
8688 try {
87- await githubClient . rest . issues . checkUserCanBeAssigned ( {
88- owner,
89- repo,
90- assignee : login ,
91- } ) ;
89+ await validateAssigneeAlias ( owner , repo , login , issueNumber , githubClient ) ;
9290 available . push ( login ) ;
9391 } catch ( e ) {
9492 const status = e && typeof e === "object" && "status" in e ? e . status : undefined ;
9593 if ( status !== 404 ) {
96- core . debug ( `Failed to check assignability for ${ login } : ${ getErrorMessage ( e ) } ` ) ;
94+ core . info ( `Failed to check assignability for ${ login } : ${ getErrorMessage ( e ) } ` ) ;
9795 }
9896 }
9997 }
10098 return available . sort ( ) ;
10199}
102100
101+ /**
102+ * Validate whether an assignee alias can be assigned in the repository context.
103+ * Prefer issue-level assignability checks when issue/PR number is available because
104+ * some agent bots are not surfaced by repository-scoped checks.
105+ * @param {string } owner
106+ * @param {string } repo
107+ * @param {string } assignee
108+ * @param {number|string|null } issueNumber
109+ * @param {Object } githubClient
110+ */
111+ async function validateAssigneeAlias ( owner , repo , assignee , issueNumber , githubClient ) {
112+ const parsedIssueNumber = Number ( issueNumber ) ;
113+ const hasValidIssueNumber = Number . isInteger ( parsedIssueNumber ) && parsedIssueNumber > 0 ;
114+ const hasIssueScopedRequest = typeof githubClient ?. request === "function" ;
115+
116+ if ( issueNumber && hasValidIssueNumber && hasIssueScopedRequest ) {
117+ core . info ( `Checking assignee alias ${ assignee } via issue-scoped endpoint for ${ owner } /${ repo } #${ parsedIssueNumber } ` ) ;
118+ try {
119+ const issueScopedResponse = await githubClient . request ( "GET /repos/{owner}/{repo}/issues/{issue_number}/assignees/{assignee}" , {
120+ owner,
121+ repo,
122+ issue_number : parsedIssueNumber ,
123+ assignee,
124+ } ) ;
125+ const issueScopedStatus = issueScopedResponse && typeof issueScopedResponse === "object" && "status" in issueScopedResponse ? Number ( issueScopedResponse . status ) : undefined ;
126+ if ( issueScopedStatus !== undefined && Number . isInteger ( issueScopedStatus ) && issueScopedStatus >= 200 && issueScopedStatus < 300 ) {
127+ core . info ( `Assignee alias ${ assignee } is assignable via issue-scoped check` ) ;
128+ return ;
129+ }
130+ core . info ( `Issue-scoped assignee check returned unexpected response for ${ assignee } (status ${ issueScopedStatus ?? "unknown" } ); falling back to repository-scoped check` ) ;
131+ } catch ( e ) {
132+ const status = e && typeof e === "object" && "status" in e ? e . status : undefined ;
133+ // Some coding-agent bot aliases can return 404 on issue-scoped checks even when
134+ // assignment may still succeed; use repository-scoped endpoint as fallback.
135+ if ( status !== 404 && status !== 422 ) {
136+ core . info ( `Issue-scoped assignee check failed for ${ assignee } with status ${ status ?? "unknown" } : ${ getErrorMessage ( e ) } ` ) ;
137+ throw e ;
138+ }
139+ core . info ( `Issue-scoped assignee check returned ${ status } for ${ assignee } ; falling back to repository-scoped check` ) ;
140+ }
141+ } else if ( issueNumber && ! hasValidIssueNumber ) {
142+ core . info ( `Skipping issue-scoped assignee check for ${ assignee } : invalid issue number ${ String ( issueNumber ) } ` ) ;
143+ } else if ( issueNumber && ! hasIssueScopedRequest ) {
144+ core . info ( `Skipping issue-scoped assignee check for ${ assignee } : github client does not support request()` ) ;
145+ }
146+ core . info ( `Checking assignee alias ${ assignee } via repository-scoped endpoint for ${ owner } /${ repo } ` ) ;
147+ await githubClient . rest . issues . checkUserCanBeAssigned ( {
148+ owner,
149+ repo,
150+ assignee,
151+ } ) ;
152+ core . info ( `Assignee alias ${ assignee } is assignable via repository-scoped check` ) ;
153+ }
154+
103155/**
104156 * Return assignable bot logins from the repository assignee list.
105157 * @param {string } owner
@@ -145,10 +197,11 @@ async function getAssignableBots(owner, repo, githubClient = github) {
145197 * @param {string } owner - Repository owner
146198 * @param {string } repo - Repository name
147199 * @param {string } agentName - Agent name (copilot)
200+ * @param {number|string|null } [issueNumber] - Optional issue/PR number for issue-scoped assignability check
148201 * @param {Object } [githubClient] - Authenticated GitHub client (defaults to global github)
149202 * @returns {Promise<string|null> } Agent ID or null if not found
150203 */
151- async function findAgent ( owner , repo , agentName , githubClient = github ) {
204+ async function findAgent ( owner , repo , agentName , issueNumber = null , githubClient = github ) {
152205 const loginNames = getAgentLogins ( agentName ) ;
153206 if ( loginNames . length === 0 ) {
154207 core . error ( `Unknown agent: ${ agentName } . Supported agents: ${ Object . keys ( AGENT_LOGIN_NAMES ) . join ( ", " ) } ` ) ;
@@ -161,11 +214,7 @@ async function findAgent(owner, repo, agentName, githubClient = github) {
161214 for ( const loginName of loginNames ) {
162215 try {
163216 core . info ( `Checking assignee alias: ${ loginName } ` ) ;
164- await githubClient . rest . issues . checkUserCanBeAssigned ( {
165- owner,
166- repo,
167- assignee : loginName ,
168- } ) ;
217+ await validateAssigneeAlias ( owner , repo , loginName , issueNumber , githubClient ) ;
169218 } catch ( checkError ) {
170219 const errorMessage = getErrorMessage ( checkError ) ;
171220 const status = checkError ?. status ;
@@ -536,7 +585,7 @@ async function assignAgentToIssueByName(owner, repo, issueNumber, agentName) {
536585 try {
537586 // Find agent using the github object authenticated via step-level github-token
538587 core . info ( `Looking for ${ agentName } coding agent...` ) ;
539- const agentId = await findAgent ( owner , repo , agentName ) ;
588+ const agentId = await findAgent ( owner , repo , agentName , issueNumber ) ;
540589 if ( ! agentId ) {
541590 return { success : false , error : `${ agentName } coding agent is not available for this repository` } ;
542591 }
0 commit comments