diff --git a/apps/open-swe/src/graphs/programmer/nodes/open-pr.ts b/apps/open-swe/src/graphs/programmer/nodes/open-pr.ts index 2d989912..f2672e7d 100644 --- a/apps/open-swe/src/graphs/programmer/nodes/open-pr.ts +++ b/apps/open-swe/src/graphs/programmer/nodes/open-pr.ts @@ -14,7 +14,7 @@ import { } from "../../../utils/github/git.js"; import { createPullRequest, - markPullRequestReadyForReview, + updatePullRequest, } from "../../../utils/github/api.js"; import { createLogger, LogLevel } from "../../../utils/logger.js"; import { z } from "zod"; @@ -44,6 +44,7 @@ import { GitHubPullRequestUpdate, } from "../../../utils/github/types.js"; import { getRepoAbsolutePath } from "@open-swe/shared/git"; +import { GITHUB_USER_LOGIN_HEADER } from "@open-swe/shared/constants"; const logger = createLogger(LogLevel.INFO, "Open PR"); @@ -180,6 +181,8 @@ export async function openPullRequest( const { title, body } = toolCall.args as z.infer; + const userLogin = config.configurable?.[GITHUB_USER_LOGIN_HEADER]; + const prForTask = getPullRequestNumberFromActiveTask( updatedTaskPlan ?? state.taskPlan, ); @@ -195,17 +198,17 @@ export async function openPullRequest( repo, headBranch: branchName, title, - body: `Fixes #${state.githubIssueId}\n\n${body}`, + body: `Fixes #${state.githubIssueId}${userLogin ? `\n\nOwner: @${userLogin}` : ""}\n\n${body}`, githubInstallationToken, baseBranch: state.targetRepository.branch, }); } else { // Ensure the PR is ready for review - pullRequest = await markPullRequestReadyForReview({ + pullRequest = await updatePullRequest({ owner, repo, title, - body: `Fixes #${state.githubIssueId}\n\n${body}`, + body: `Fixes #${state.githubIssueId}${userLogin ? `\n\nOwner: @${userLogin}` : ""}\n\n${body}`, pullNumber: prForTask, githubInstallationToken, }); diff --git a/apps/open-swe/src/utils/github/api.ts b/apps/open-swe/src/utils/github/api.ts index aaf0a3c5..82f91e0a 100644 --- a/apps/open-swe/src/utils/github/api.ts +++ b/apps/open-swe/src/utils/github/api.ts @@ -317,6 +317,44 @@ export async function markPullRequestReadyForReview({ ); } +export async function updatePullRequest({ + owner, + repo, + pullNumber, + title, + body, + githubInstallationToken, +}: { + owner: string; + repo: string; + pullNumber: number; + title?: string; + body?: string; + githubInstallationToken: string; +}) { + return withGitHubRetry( + async (token: string) => { + const octokit = new Octokit({ + auth: token, + }); + + const { data: pullRequest } = await octokit.pulls.update({ + owner, + repo, + pull_number: pullNumber, + ...(title && { title }), + ...(body && { body }), + }); + + return pullRequest; + }, + githubInstallationToken, + "Failed to update pull request", + { pullNumber, owner, repo }, + 1, + ); +} + export async function getIssue({ owner, repo, diff --git a/apps/web/src/components/v2/terminal-input.tsx b/apps/web/src/components/v2/terminal-input.tsx index f1e4d127..985f9110 100644 --- a/apps/web/src/components/v2/terminal-input.tsx +++ b/apps/web/src/components/v2/terminal-input.tsx @@ -16,6 +16,7 @@ import { toast } from "sonner"; import { DEFAULT_CONFIG_KEY, useConfigStore } from "@/hooks/useConfigStore"; import { API_KEY_REQUIRED_MESSAGE, + GITHUB_USER_LOGIN_HEADER, MANAGER_GRAPH_ID, } from "@open-swe/shared/constants"; import { ManagerGraphUpdate } from "@open-swe/shared/open-swe/manager/types"; @@ -163,6 +164,7 @@ export function TerminalInput({ recursion_limit: 400, configurable: { ...defaultConfig, + [GITHUB_USER_LOGIN_HEADER]: user.login, }, }, ifNotExists: "create",