From 68cf7905d14b9fcaad87a8103c398857f9da78a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:10:37 +0000 Subject: [PATCH 1/2] Initial plan From 62ee1032be6fd7d7154e901655bac453c8c99c9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:21:42 +0000 Subject: [PATCH 2/2] fix: Use simple GraphQL mutation for backward compatibility when no Copilot options provided Co-authored-by: mrjf <180956+mrjf@users.noreply.github.com> --- .github/workflows/ai-triage-campaign.lock.yml | 92 +++++++++------ .../breaking-change-checker.lock.yml | 92 +++++++++------ .../copilot-pr-merged-report.lock.yml | 27 ++++- .../duplicate-code-detector.lock.yml | 92 +++++++++------ .github/workflows/issue-monster.lock.yml | 92 +++++++++------ docs/src/content/docs/labs.mdx | 1 + pkg/workflow/js/assign_agent_helpers.cjs | 106 +++++++++++------- pkg/workflow/js/assign_agent_helpers.test.cjs | 38 ++++++- 8 files changed, 338 insertions(+), 202 deletions(-) diff --git a/.github/workflows/ai-triage-campaign.lock.yml b/.github/workflows/ai-triage-campaign.lock.yml index 9b47a64597..af93187e08 100644 --- a/.github/workflows/ai-triage-campaign.lock.yml +++ b/.github/workflows/ai-triage-campaign.lock.yml @@ -5193,45 +5193,63 @@ jobs: actorIds.push(assigneeId); } } - const mutationInput = { - assignableId: issueId, - actorIds: actorIds, - }; - if (options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent) { - const copilotOptions = {}; - if (options.targetRepositoryId) { - copilotOptions.targetRepositoryId = options.targetRepositoryId; - } - if (options.baseBranch) { - copilotOptions.baseBranch = options.baseBranch; - } - if (options.customInstructions) { - copilotOptions.customInstructions = options.customInstructions; - } - if (options.customAgent) { - copilotOptions.customAgent = options.customAgent; - } - mutationInput.copilotAssignmentOptions = copilotOptions; - } - const mutation = ` - mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { - replaceActorsForAssignable(input: { - assignableId: $assignableId, - actorIds: $actorIds, - copilotAssignmentOptions: $copilotAssignmentOptions - }) { - __typename - } - } - `; + const hasCopilotOptions = options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent; try { core.info("Using built-in github object for mutation"); - core.debug(`GraphQL mutation with variables: ${JSON.stringify(mutationInput)}`); - const response = await github.graphql(mutation, mutationInput, { - headers: { - "GraphQL-Features": "issues_copilot_assignment_api_support", - }, - }); + let response; + if (hasCopilotOptions) { + const copilotOptions = {}; + if (options.targetRepositoryId) { + copilotOptions.targetRepositoryId = options.targetRepositoryId; + } + if (options.baseBranch) { + copilotOptions.baseBranch = options.baseBranch; + } + if (options.customInstructions) { + copilotOptions.customInstructions = options.customInstructions; + } + if (options.customAgent) { + copilotOptions.customAgent = options.customAgent; + } + const extendedMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds, + copilotAssignmentOptions: $copilotAssignmentOptions + }) { + __typename + } + } + `; + const mutationInput = { + assignableId: issueId, + actorIds: actorIds, + copilotAssignmentOptions: copilotOptions, + }; + core.debug(`GraphQL mutation with Copilot options: ${JSON.stringify(mutationInput)}`); + response = await github.graphql(extendedMutation, mutationInput, { + headers: { + "GraphQL-Features": "issues_copilot_assignment_api_support", + }, + }); + } else { + const simpleMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds + }) { + __typename + } + } + `; + core.debug(`GraphQL mutation with variables: assignableId=${issueId}, actorIds=${JSON.stringify(actorIds)}`); + response = await github.graphql(simpleMutation, { + assignableId: issueId, + actorIds: actorIds, + }); + } if (response && response.replaceActorsForAssignable && response.replaceActorsForAssignable.__typename) { return true; } else { diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml index a1396f1db8..0c587ece16 100644 --- a/.github/workflows/breaking-change-checker.lock.yml +++ b/.github/workflows/breaking-change-checker.lock.yml @@ -6397,45 +6397,63 @@ jobs: actorIds.push(assigneeId); } } - const mutationInput = { - assignableId: issueId, - actorIds: actorIds, - }; - if (options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent) { - const copilotOptions = {}; - if (options.targetRepositoryId) { - copilotOptions.targetRepositoryId = options.targetRepositoryId; - } - if (options.baseBranch) { - copilotOptions.baseBranch = options.baseBranch; - } - if (options.customInstructions) { - copilotOptions.customInstructions = options.customInstructions; - } - if (options.customAgent) { - copilotOptions.customAgent = options.customAgent; - } - mutationInput.copilotAssignmentOptions = copilotOptions; - } - const mutation = ` - mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { - replaceActorsForAssignable(input: { - assignableId: $assignableId, - actorIds: $actorIds, - copilotAssignmentOptions: $copilotAssignmentOptions - }) { - __typename - } - } - `; + const hasCopilotOptions = options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent; try { core.info("Using built-in github object for mutation"); - core.debug(`GraphQL mutation with variables: ${JSON.stringify(mutationInput)}`); - const response = await github.graphql(mutation, mutationInput, { - headers: { - "GraphQL-Features": "issues_copilot_assignment_api_support", - }, - }); + let response; + if (hasCopilotOptions) { + const copilotOptions = {}; + if (options.targetRepositoryId) { + copilotOptions.targetRepositoryId = options.targetRepositoryId; + } + if (options.baseBranch) { + copilotOptions.baseBranch = options.baseBranch; + } + if (options.customInstructions) { + copilotOptions.customInstructions = options.customInstructions; + } + if (options.customAgent) { + copilotOptions.customAgent = options.customAgent; + } + const extendedMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds, + copilotAssignmentOptions: $copilotAssignmentOptions + }) { + __typename + } + } + `; + const mutationInput = { + assignableId: issueId, + actorIds: actorIds, + copilotAssignmentOptions: copilotOptions, + }; + core.debug(`GraphQL mutation with Copilot options: ${JSON.stringify(mutationInput)}`); + response = await github.graphql(extendedMutation, mutationInput, { + headers: { + "GraphQL-Features": "issues_copilot_assignment_api_support", + }, + }); + } else { + const simpleMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds + }) { + __typename + } + } + `; + core.debug(`GraphQL mutation with variables: assignableId=${issueId}, actorIds=${JSON.stringify(actorIds)}`); + response = await github.graphql(simpleMutation, { + assignableId: issueId, + actorIds: actorIds, + }); + } if (response && response.replaceActorsForAssignable && response.replaceActorsForAssignable.__typename) { return true; } else { diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index 477828dff0..e77363b891 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -1844,7 +1844,7 @@ jobs: EOF chmod +x /tmp/gh-aw/safeoutputs/mcp-server.cjs - - name: Setup Safe Inputs MCP + - name: Setup Safe Inputs JavaScript and Config run: | mkdir -p /tmp/gh-aw/safe-inputs/logs cat > /tmp/gh-aw/safe-inputs/read_buffer.cjs << 'EOF_READ_BUFFER' @@ -2549,6 +2549,9 @@ jobs: }); EOFSI chmod +x /tmp/gh-aw/safe-inputs/mcp-server.cjs + + - name: Setup Safe Inputs Tool Files + run: | cat > /tmp/gh-aw/safe-inputs/gh.sh << 'EOFSH_gh' #!/bin/bash # Auto-generated safe-input tool: gh @@ -5772,7 +5775,11 @@ jobs: if (validDeniedRequests > 0) { - summary += `**${validDeniedRequests}** request${validDeniedRequests !== 1 ? "s" : ""} blocked across **${validDeniedDomains.length}** unique domain${validDeniedDomains.length !== 1 ? "s" : ""}`; + summary += `**${validDeniedRequests}** request${validDeniedRequests !== 1 ? "s" : ""} blocked across **${ + + validDeniedDomains.length + + }** unique domain${validDeniedDomains.length !== 1 ? "s" : ""}`; summary += ` (${totalRequests > 0 ? Math.round((validDeniedRequests / totalRequests) * 100) : 0}% of total traffic)\n\n`; @@ -6004,7 +6011,9 @@ jobs: } const level = extractLevel(match, pattern); const message = extractMessage(match, pattern, line); - const errorMessage = `Line ${lineIndex + 1}: ${message} (Pattern: ${pattern.description || "Unknown pattern"}, Raw log: ${truncateString(line.trim(), 120)})`; + const errorMessage = `Line ${lineIndex + 1}: ${message} (Pattern: ${ + pattern.description || "Unknown pattern" + }, Raw log: ${truncateString(line.trim(), 120)})`; if (level.toLowerCase() === "error") { core.error(errorMessage); hasErrors = true; @@ -6954,7 +6963,9 @@ jobs: } return { valid: false, - error: `Repository '${repo}' is not in the allowed-repos list. Allowed: ${defaultRepo}${allowedRepos.size > 0 ? ", " + Array.from(allowedRepos).join(", ") : ""}`, + error: `Repository '${repo}' is not in the allowed-repos list. Allowed: ${defaultRepo}${ + allowedRepos.size > 0 ? ", " + Array.from(allowedRepos).join(", ") : "" + }`, }; } function parseRepoSlug(repoSlug) { @@ -7106,7 +7117,9 @@ jobs: repoInfo = fetchedInfo; repoInfoCache.set(itemRepo, repoInfo); core.info( - `Fetched discussion categories for ${itemRepo}: ${JSON.stringify(repoInfo.discussionCategories.map(cat => ({ name: cat.name, id: cat.id })))}` + `Fetched discussion categories for ${itemRepo}: ${JSON.stringify( + repoInfo.discussionCategories.map(cat => ({ name: cat.name, id: cat.id })) + )}` ); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); @@ -7144,7 +7157,9 @@ jobs: } const categoryId = categoryInfo.id; core.info( - `Processing create-discussion item ${i + 1}/${createDiscussionItems.length}: title=${createDiscussionItem.title}, bodyLength=${createDiscussionItem.body?.length || 0}, repo=${itemRepo}` + `Processing create-discussion item ${i + 1}/${createDiscussionItems.length}: title=${createDiscussionItem.title}, bodyLength=${ + createDiscussionItem.body?.length || 0 + }, repo=${itemRepo}` ); let title = createDiscussionItem.title ? replaceTemporaryIdReferences(createDiscussionItem.title.trim(), temporaryIdMap, itemRepo) : ""; const bodyText = createDiscussionItem.body || ""; diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index a6de7a1abe..b883a6dff2 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -5959,45 +5959,63 @@ jobs: actorIds.push(assigneeId); } } - const mutationInput = { - assignableId: issueId, - actorIds: actorIds, - }; - if (options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent) { - const copilotOptions = {}; - if (options.targetRepositoryId) { - copilotOptions.targetRepositoryId = options.targetRepositoryId; - } - if (options.baseBranch) { - copilotOptions.baseBranch = options.baseBranch; - } - if (options.customInstructions) { - copilotOptions.customInstructions = options.customInstructions; - } - if (options.customAgent) { - copilotOptions.customAgent = options.customAgent; - } - mutationInput.copilotAssignmentOptions = copilotOptions; - } - const mutation = ` - mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { - replaceActorsForAssignable(input: { - assignableId: $assignableId, - actorIds: $actorIds, - copilotAssignmentOptions: $copilotAssignmentOptions - }) { - __typename - } - } - `; + const hasCopilotOptions = options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent; try { core.info("Using built-in github object for mutation"); - core.debug(`GraphQL mutation with variables: ${JSON.stringify(mutationInput)}`); - const response = await github.graphql(mutation, mutationInput, { - headers: { - "GraphQL-Features": "issues_copilot_assignment_api_support", - }, - }); + let response; + if (hasCopilotOptions) { + const copilotOptions = {}; + if (options.targetRepositoryId) { + copilotOptions.targetRepositoryId = options.targetRepositoryId; + } + if (options.baseBranch) { + copilotOptions.baseBranch = options.baseBranch; + } + if (options.customInstructions) { + copilotOptions.customInstructions = options.customInstructions; + } + if (options.customAgent) { + copilotOptions.customAgent = options.customAgent; + } + const extendedMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds, + copilotAssignmentOptions: $copilotAssignmentOptions + }) { + __typename + } + } + `; + const mutationInput = { + assignableId: issueId, + actorIds: actorIds, + copilotAssignmentOptions: copilotOptions, + }; + core.debug(`GraphQL mutation with Copilot options: ${JSON.stringify(mutationInput)}`); + response = await github.graphql(extendedMutation, mutationInput, { + headers: { + "GraphQL-Features": "issues_copilot_assignment_api_support", + }, + }); + } else { + const simpleMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds + }) { + __typename + } + } + `; + core.debug(`GraphQL mutation with variables: assignableId=${issueId}, actorIds=${JSON.stringify(actorIds)}`); + response = await github.graphql(simpleMutation, { + assignableId: issueId, + actorIds: actorIds, + }); + } if (response && response.replaceActorsForAssignable && response.replaceActorsForAssignable.__typename) { return true; } else { diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index d7617713e2..a86e327e5c 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -6083,45 +6083,63 @@ jobs: actorIds.push(assigneeId); } } - const mutationInput = { - assignableId: issueId, - actorIds: actorIds, - }; - if (options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent) { - const copilotOptions = {}; - if (options.targetRepositoryId) { - copilotOptions.targetRepositoryId = options.targetRepositoryId; - } - if (options.baseBranch) { - copilotOptions.baseBranch = options.baseBranch; - } - if (options.customInstructions) { - copilotOptions.customInstructions = options.customInstructions; - } - if (options.customAgent) { - copilotOptions.customAgent = options.customAgent; - } - mutationInput.copilotAssignmentOptions = copilotOptions; - } - const mutation = ` - mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { - replaceActorsForAssignable(input: { - assignableId: $assignableId, - actorIds: $actorIds, - copilotAssignmentOptions: $copilotAssignmentOptions - }) { - __typename - } - } - `; + const hasCopilotOptions = options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent; try { core.info("Using built-in github object for mutation"); - core.debug(`GraphQL mutation with variables: ${JSON.stringify(mutationInput)}`); - const response = await github.graphql(mutation, mutationInput, { - headers: { - "GraphQL-Features": "issues_copilot_assignment_api_support", - }, - }); + let response; + if (hasCopilotOptions) { + const copilotOptions = {}; + if (options.targetRepositoryId) { + copilotOptions.targetRepositoryId = options.targetRepositoryId; + } + if (options.baseBranch) { + copilotOptions.baseBranch = options.baseBranch; + } + if (options.customInstructions) { + copilotOptions.customInstructions = options.customInstructions; + } + if (options.customAgent) { + copilotOptions.customAgent = options.customAgent; + } + const extendedMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds, + copilotAssignmentOptions: $copilotAssignmentOptions + }) { + __typename + } + } + `; + const mutationInput = { + assignableId: issueId, + actorIds: actorIds, + copilotAssignmentOptions: copilotOptions, + }; + core.debug(`GraphQL mutation with Copilot options: ${JSON.stringify(mutationInput)}`); + response = await github.graphql(extendedMutation, mutationInput, { + headers: { + "GraphQL-Features": "issues_copilot_assignment_api_support", + }, + }); + } else { + const simpleMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds + }) { + __typename + } + } + `; + core.debug(`GraphQL mutation with variables: assignableId=${issueId}, actorIds=${JSON.stringify(actorIds)}`); + response = await github.graphql(simpleMutation, { + assignableId: issueId, + actorIds: actorIds, + }); + } if (response && response.replaceActorsForAssignable && response.replaceActorsForAssignable.__typename) { return true; } else { diff --git a/docs/src/content/docs/labs.mdx b/docs/src/content/docs/labs.mdx index e7cf013608..d25ec3c891 100644 --- a/docs/src/content/docs/labs.mdx +++ b/docs/src/content/docs/labs.mdx @@ -31,6 +31,7 @@ These are experimental agentic workflows used by the GitHub Next team to learn, | [Copilot PR Prompt Pattern Analysis](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/copilot-pr-prompt-analysis.md) | copilot | [![Copilot PR Prompt Pattern Analysis](https://github.com/githubnext/gh-aw/actions/workflows/copilot-pr-prompt-analysis.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/copilot-pr-prompt-analysis.lock.yml) | `0 9 * * *` | - | | [Copilot Session Insights](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/copilot-session-insights.md) | claude | [![Copilot Session Insights](https://github.com/githubnext/gh-aw/actions/workflows/copilot-session-insights.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/copilot-session-insights.lock.yml) | `0 16 * * *` | - | | [Daily Code Metrics and Trend Tracking Agent](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-code-metrics.md) | claude | [![Daily Code Metrics and Trend Tracking Agent](https://github.com/githubnext/gh-aw/actions/workflows/daily-code-metrics.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-code-metrics.lock.yml) | `0 8 * * *` | - | +| [Daily Copilot PR Merged Report](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/copilot-pr-merged-report.md) | copilot | [![Daily Copilot PR Merged Report](https://github.com/githubnext/gh-aw/actions/workflows/copilot-pr-merged-report.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/copilot-pr-merged-report.lock.yml) | `0 15 * * 1-5` | - | | [Daily Copilot Token Consumption Report](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-copilot-token-report.md) | copilot | [![Daily Copilot Token Consumption Report](https://github.com/githubnext/gh-aw/actions/workflows/daily-copilot-token-report.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-copilot-token-report.lock.yml) | `0 11 * * 1-5` | - | | [Daily Documentation Updater](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-doc-updater.md) | claude | [![Daily Documentation Updater](https://github.com/githubnext/gh-aw/actions/workflows/daily-doc-updater.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-doc-updater.lock.yml) | `0 6 * * *` | - | | [Daily Fact About gh-aw](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-fact.md) | codex | [![Daily Fact About gh-aw](https://github.com/githubnext/gh-aw/actions/workflows/daily-fact.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-fact.lock.yml) | `0 11 * * 1-5` | - | diff --git a/pkg/workflow/js/assign_agent_helpers.cjs b/pkg/workflow/js/assign_agent_helpers.cjs index 7c93eda62a..a3bf5e8486 100644 --- a/pkg/workflow/js/assign_agent_helpers.cjs +++ b/pkg/workflow/js/assign_agent_helpers.cjs @@ -223,56 +223,78 @@ async function assignAgentToIssue(issueId, agentId, currentAssignees, agentName, } } - // Build mutation input with optional Copilot assignment options - const mutationInput = { - assignableId: issueId, - actorIds: actorIds, - }; - - // Add Copilot-specific assignment options if provided - if (options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent) { - const copilotOptions = {}; - - if (options.targetRepositoryId) { - copilotOptions.targetRepositoryId = options.targetRepositoryId; - } + // Check if any Copilot-specific options are provided + const hasCopilotOptions = options.targetRepositoryId || options.baseBranch || options.customInstructions || options.customAgent; - if (options.baseBranch) { - copilotOptions.baseBranch = options.baseBranch; - } + try { + core.info("Using built-in github object for mutation"); - if (options.customInstructions) { - copilotOptions.customInstructions = options.customInstructions; - } + let response; - if (options.customAgent) { - copilotOptions.customAgent = options.customAgent; - } + if (hasCopilotOptions) { + // Build Copilot assignment options + const copilotOptions = {}; - mutationInput.copilotAssignmentOptions = copilotOptions; - } + if (options.targetRepositoryId) { + copilotOptions.targetRepositoryId = options.targetRepositoryId; + } - const mutation = ` - mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { - replaceActorsForAssignable(input: { - assignableId: $assignableId, - actorIds: $actorIds, - copilotAssignmentOptions: $copilotAssignmentOptions - }) { - __typename + if (options.baseBranch) { + copilotOptions.baseBranch = options.baseBranch; } - } - `; - try { - core.info("Using built-in github object for mutation"); + if (options.customInstructions) { + copilotOptions.customInstructions = options.customInstructions; + } + + if (options.customAgent) { + copilotOptions.customAgent = options.customAgent; + } + + // Use extended mutation with Copilot assignment options + const extendedMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!, $copilotAssignmentOptions: CopilotAssignmentOptionsInput) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds, + copilotAssignmentOptions: $copilotAssignmentOptions + }) { + __typename + } + } + `; + + const mutationInput = { + assignableId: issueId, + actorIds: actorIds, + copilotAssignmentOptions: copilotOptions, + }; + + core.debug(`GraphQL mutation with Copilot options: ${JSON.stringify(mutationInput)}`); + response = await github.graphql(extendedMutation, mutationInput, { + headers: { + "GraphQL-Features": "issues_copilot_assignment_api_support", + }, + }); + } else { + // Use simple mutation for backward compatibility (no Copilot-specific options) + const simpleMutation = ` + mutation($assignableId: ID!, $actorIds: [ID!]!) { + replaceActorsForAssignable(input: { + assignableId: $assignableId, + actorIds: $actorIds + }) { + __typename + } + } + `; - core.debug(`GraphQL mutation with variables: ${JSON.stringify(mutationInput)}`); - const response = await github.graphql(mutation, mutationInput, { - headers: { - "GraphQL-Features": "issues_copilot_assignment_api_support", - }, - }); + core.debug(`GraphQL mutation with variables: assignableId=${issueId}, actorIds=${JSON.stringify(actorIds)}`); + response = await github.graphql(simpleMutation, { + assignableId: issueId, + actorIds: actorIds, + }); + } if (response && response.replaceActorsForAssignable && response.replaceActorsForAssignable.__typename) { return true; diff --git a/pkg/workflow/js/assign_agent_helpers.test.cjs b/pkg/workflow/js/assign_agent_helpers.test.cjs index bea1b2c8ba..125d463128 100644 --- a/pkg/workflow/js/assign_agent_helpers.test.cjs +++ b/pkg/workflow/js/assign_agent_helpers.test.cjs @@ -245,7 +245,7 @@ describe("assign_agent_helpers.cjs", () => { }); describe("assignAgentToIssue", () => { - it("should successfully assign agent using mutation", async () => { + it("should successfully assign agent using simple mutation (no options)", async () => { // Mock the global github.graphql mockGithub.graphql.mockResolvedValueOnce({ replaceActorsForAssignable: { @@ -256,11 +256,42 @@ describe("assign_agent_helpers.cjs", () => { const result = await assignAgentToIssue("ISSUE_123", "AGENT_456", ["USER_1"], "copilot"); expect(result).toBe(true); + // Simple mutation without options should not include headers expect(mockGithub.graphql).toHaveBeenCalledWith( expect.stringContaining("replaceActorsForAssignable"), expect.objectContaining({ assignableId: "ISSUE_123", actorIds: ["AGENT_456", "USER_1"], + }) + ); + // Verify no extra arguments (no headers) for simple mutation + expect(mockGithub.graphql.mock.calls[0].length).toBe(2); + }); + + it("should use extended mutation with headers when Copilot options provided", async () => { + mockGithub.graphql.mockResolvedValueOnce({ + replaceActorsForAssignable: { + __typename: "ReplaceActorsForAssignablePayload", + }, + }); + + const options = { + baseBranch: "main", + customInstructions: "Test instructions", + }; + + const result = await assignAgentToIssue("ISSUE_123", "AGENT_456", ["USER_1"], "copilot", options); + + expect(result).toBe(true); + expect(mockGithub.graphql).toHaveBeenCalledWith( + expect.stringContaining("replaceActorsForAssignable"), + expect.objectContaining({ + assignableId: "ISSUE_123", + actorIds: ["AGENT_456", "USER_1"], + copilotAssignmentOptions: expect.objectContaining({ + baseBranch: "main", + customInstructions: "Test instructions", + }), }), expect.objectContaining({ headers: expect.objectContaining({ @@ -284,11 +315,6 @@ describe("assign_agent_helpers.cjs", () => { expect.objectContaining({ assignableId: "ISSUE_123", actorIds: expect.arrayContaining(["AGENT_456", "USER_1", "USER_2"]), - }), - expect.objectContaining({ - headers: expect.objectContaining({ - "GraphQL-Features": "issues_copilot_assignment_api_support", - }), }) ); });