diff --git a/actions/setup/js/assign_agent_helpers.cjs b/actions/setup/js/assign_agent_helpers.cjs index af5676d9840..dcf2c838275 100644 --- a/actions/setup/js/assign_agent_helpers.cjs +++ b/actions/setup/js/assign_agent_helpers.cjs @@ -403,6 +403,13 @@ async function assignAgentToIssue( issue_number: issueNumber, assignees: [assignee], }; + const agentAssignment = {}; + if (pullRequestRepoSlug != null) agentAssignment.target_repo = pullRequestRepoSlug; + if (baseBranch != null) agentAssignment.base_branch = baseBranch; + if (customInstructions != null) agentAssignment.custom_instructions = customInstructions; + if (customAgent != null) agentAssignment.custom_agent = customAgent; + if (model != null) agentAssignment.model = model; + if (Object.keys(agentAssignment).length > 0) assignParams.agent_assignment = agentAssignment; await githubClient.request("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", assignParams); return true; } catch (error) { diff --git a/actions/setup/js/assign_agent_helpers.test.cjs b/actions/setup/js/assign_agent_helpers.test.cjs index f161106b9e5..84c5784092f 100644 --- a/actions/setup/js/assign_agent_helpers.test.cjs +++ b/actions/setup/js/assign_agent_helpers.test.cjs @@ -456,6 +456,96 @@ describe("assign_agent_helpers.cjs", () => { expect(mockCore.error).toHaveBeenCalledWith(expect.stringContaining("GH_AW_AGENT_TOKEN")); expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("github.github.com/gh-aw/reference/copilot-cloud-agent/#authentication")); }); + + it("should not include agent_assignment when all optional fields are null", async () => { + const mockRequest = vi.fn().mockResolvedValue({ status: 201 }); + const restClient = { request: mockRequest }; + + await assignAgentToIssue("id", "copilot-swe-agent[bot]", [], "copilot", null, null, null, null, null, restClient, taskContext); + + expect(mockRequest).toHaveBeenCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", { + owner: "myorg", + repo: "myrepo", + issue_number: 42, + assignees: ["copilot-swe-agent[bot]"], + }); + }); + + it("should include agent_assignment with all fields when all are provided", async () => { + const mockRequest = vi.fn().mockResolvedValue({ status: 201 }); + const restClient = { request: mockRequest }; + + await assignAgentToIssue("id", "copilot-swe-agent[bot]", [], "copilot", null, "claude-opus-4.6", "my-agent", "Follow the coding guidelines.", "feature-branch", restClient, taskContext, "otherorg/otherrepo"); + + expect(mockRequest).toHaveBeenCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", { + owner: "myorg", + repo: "myrepo", + issue_number: 42, + assignees: ["copilot-swe-agent[bot]"], + agent_assignment: { + target_repo: "otherorg/otherrepo", + base_branch: "feature-branch", + custom_instructions: "Follow the coding guidelines.", + custom_agent: "my-agent", + model: "claude-opus-4.6", + }, + }); + }); + + it("should include agent_assignment with only the provided fields", async () => { + const mockRequest = vi.fn().mockResolvedValue({ status: 201 }); + const restClient = { request: mockRequest }; + + await assignAgentToIssue("id", "copilot-swe-agent[bot]", [], "copilot", null, null, null, "Always write tests.", null, restClient, taskContext); + + expect(mockRequest).toHaveBeenCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", { + owner: "myorg", + repo: "myrepo", + issue_number: 42, + assignees: ["copilot-swe-agent[bot]"], + agent_assignment: { + custom_instructions: "Always write tests.", + }, + }); + }); + + it("should include agent_assignment with target_repo when pullRequestRepoSlug is provided", async () => { + const mockRequest = vi.fn().mockResolvedValue({ status: 201 }); + const restClient = { request: mockRequest }; + + await assignAgentToIssue("id", "copilot-swe-agent[bot]", [], "copilot", null, null, null, null, null, restClient, taskContext, "otherorg/otherrepo"); + + expect(mockRequest).toHaveBeenCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", { + owner: "myorg", + repo: "myrepo", + issue_number: 42, + assignees: ["copilot-swe-agent[bot]"], + agent_assignment: { + target_repo: "otherorg/otherrepo", + }, + }); + }); + + it("should include empty-string agent_assignment fields when explicitly provided", async () => { + const mockRequest = vi.fn().mockResolvedValue({ status: 201 }); + const restClient = { request: mockRequest }; + + await assignAgentToIssue("id", "copilot-swe-agent[bot]", [], "copilot", null, "", "", "", "", restClient, taskContext, ""); + + expect(mockRequest).toHaveBeenCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", { + owner: "myorg", + repo: "myrepo", + issue_number: 42, + assignees: ["copilot-swe-agent[bot]"], + agent_assignment: { + target_repo: "", + base_branch: "", + custom_instructions: "", + custom_agent: "", + model: "", + }, + }); + }); }); describe("generatePermissionErrorSummary", () => { diff --git a/actions/setup/js/assign_to_agent.cjs b/actions/setup/js/assign_to_agent.cjs index 064a7fab4e2..21868685ce5 100644 --- a/actions/setup/js/assign_to_agent.cjs +++ b/actions/setup/js/assign_to_agent.cjs @@ -244,6 +244,7 @@ async function main(config = {}) { // Handle per-item pull_request_repo override let effectivePullRequestRepoSlug = basePullRequestRepoSlug; + let effectivePullRequestBaseBranch = effectiveBaseBranch; let hasValidatedPerItemPullRequestRepoOverride = false; const hasPullRequestRepoOverrideField = message.pull_request_repo != null; const trimmedPullRequestRepoOverride = typeof message.pull_request_repo === "string" ? message.pull_request_repo.trim() : ""; @@ -260,8 +261,9 @@ async function main(config = {}) { return { success: false, error }; } try { - await resolvePullRequestRepo(githubClient, pullRequestRepoParts[0], pullRequestRepoParts[1], configuredBaseBranch); + const resolvedPullRequestRepo = await resolvePullRequestRepo(githubClient, pullRequestRepoParts[0], pullRequestRepoParts[1], configuredBaseBranch); effectivePullRequestRepoSlug = itemPullRequestRepo; + effectivePullRequestBaseBranch = resolvedPullRequestRepo.effectiveBaseBranch; hasValidatedPerItemPullRequestRepoOverride = true; core.info(`Using per-item pull request repository: ${itemPullRequestRepo}`); } catch (error) { @@ -389,7 +391,7 @@ async function main(config = {}) { if (model) core.info(`Using model: ${model}`); if (customAgent) core.info(`Using custom agent: ${customAgent}`); if (customInstructions) core.info(`Using custom instructions: ${customInstructions.substring(0, 100)}${customInstructions.length > 100 ? "..." : ""}`); - if (effectiveBaseBranch) core.info(`Using base branch: ${effectiveBaseBranch}`); + if (effectivePullRequestBaseBranch) core.info(`Using base branch: ${effectivePullRequestBaseBranch}`); const success = await assignAgentToIssue( assignableId, @@ -400,7 +402,7 @@ async function main(config = {}) { model, customAgent, customInstructions, - effectiveBaseBranch, + effectivePullRequestBaseBranch, githubClient, taskContext, effectivePullRequestRepoSlug, diff --git a/actions/setup/js/assign_to_agent.test.cjs b/actions/setup/js/assign_to_agent.test.cjs index fd9832bcff6..d2b4731824a 100644 --- a/actions/setup/js/assign_to_agent.test.cjs +++ b/actions/setup/js/assign_to_agent.test.cjs @@ -1496,7 +1496,7 @@ describe("assign_to_agent", () => { // Get global PR repository ID and default branch (for default-pr-repo) mockGithub.rest.repos.get.mockResolvedValueOnce({ data: { node_id: "default-pr-repo-id", default_branch: "main" } }); // Get item PR repository - mockGithub.rest.repos.get.mockResolvedValueOnce({ data: { node_id: "item-pull-request-repo-id", default_branch: "main" } }); + mockGithub.rest.repos.get.mockResolvedValueOnce({ data: { node_id: "item-pull-request-repo-id", default_branch: "develop" } }); // Find agent mockGithub.rest.issues.checkUserCanBeAssigned.mockResolvedValueOnce({}); mockGithub.rest.users.getByUsername.mockResolvedValueOnce({ data: { id: 99999 } }); @@ -1511,7 +1511,16 @@ describe("assign_to_agent", () => { expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Using per-item pull request repository: test-owner/item-pull-request-repo")); - expect(mockGithub.request).toHaveBeenCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", expect.objectContaining({ owner: "test-owner", repo: "test-repo", issue_number: 42 })); + expect(mockGithub.request).toHaveBeenLastCalledWith("POST /repos/{owner}/{repo}/issues/{issue_number}/assignees", { + owner: "test-owner", + repo: "test-repo", + issue_number: 42, + assignees: ["copilot-swe-agent[bot]"], + agent_assignment: { + target_repo: "test-owner/item-pull-request-repo", + base_branch: "develop", + }, + }); }); it("should reject per-item pull_request_repo not in allowed list", async () => {