feat(agents): add PR title, draft, and status icons to sidebar (#22952)

Adds `pull_request_title` and `pull_request_draft` to the chat diff
status pipeline (DB → provider → SDK → frontend). The GitHub provider
now fetches the PR title alongside existing status fields.

The agents sidebar now displays PR-state-aware icons for chats that have
a linked pull request (when the chat is in waiting/completed state):
- **Open PR**: `GitPullRequestArrow` (green)
- **Draft PR**: `GitPullRequestDraft` (gray)
- **Merged PR**: `GitMerge` (purple)
- **Closed PR**: `GitPullRequestClosed` (red)

Running/pending/paused/error chats keep their existing activity icons
(spinner, pause, error triangle).

### Changes

**Database migration** (`000432`): Adds `pull_request_title TEXT` and
`pull_request_draft BOOLEAN` columns to `chat_diff_statuses`.

**Backend pipeline**:
- `gitprovider.PRStatus` gains a `Title` field
- GitHub provider decodes the `title` from the API response
- `gitsync` and `coderd/chats.go` pass title + draft through to the DB
upsert
- `codersdk.ChatDiffStatus` exposes both new fields in the API response

**Frontend** (`AgentsSidebar.tsx`): New `getPRIconConfig()` function
resolves the appropriate Lucide git icon based on `pull_request_state`
and `pull_request_draft`. Only applies when the chat is in a terminal
state (waiting/completed).

**Real-time sync**: No changes needed — the existing
`diff_status_change` pubsub event already propagates the full
`ChatDiffStatus` including the new fields.
This commit is contained in:
Kyle Carberry
2026-03-11 11:50:45 -04:00
committed by GitHub
parent a46336c3ec
commit 7a83d825cf
16 changed files with 214 additions and 14 deletions
+3 -1
View File
@@ -1187,7 +1187,9 @@ CREATE TABLE chat_diff_statuses (
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL,
git_branch text DEFAULT ''::text NOT NULL,
git_remote_origin text DEFAULT ''::text NOT NULL
git_remote_origin text DEFAULT ''::text NOT NULL,
pull_request_title text DEFAULT ''::text NOT NULL,
pull_request_draft boolean DEFAULT false NOT NULL
);
CREATE TABLE chat_files (
@@ -0,0 +1,2 @@
ALTER TABLE chat_diff_statuses DROP COLUMN pull_request_title;
ALTER TABLE chat_diff_statuses DROP COLUMN pull_request_draft;
@@ -0,0 +1,2 @@
ALTER TABLE chat_diff_statuses ADD COLUMN pull_request_title TEXT NOT NULL DEFAULT '';
ALTER TABLE chat_diff_statuses ADD COLUMN pull_request_draft BOOLEAN NOT NULL DEFAULT FALSE;
+2
View File
@@ -3924,6 +3924,8 @@ type ChatDiffStatus struct {
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
GitBranch string `db:"git_branch" json:"git_branch"`
GitRemoteOrigin string `db:"git_remote_origin" json:"git_remote_origin"`
PullRequestTitle string `db:"pull_request_title" json:"pull_request_title"`
PullRequestDraft bool `db:"pull_request_draft" json:"pull_request_draft"`
}
type ChatFile struct {
+33 -11
View File
@@ -3056,10 +3056,10 @@ WITH acquired AS (
LIMIT
$1::int
)
RETURNING chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin
RETURNING chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin, pull_request_title, pull_request_draft
)
SELECT
acquired.chat_id, acquired.url, acquired.pull_request_state, acquired.changes_requested, acquired.additions, acquired.deletions, acquired.changed_files, acquired.refreshed_at, acquired.stale_at, acquired.created_at, acquired.updated_at, acquired.git_branch, acquired.git_remote_origin,
acquired.chat_id, acquired.url, acquired.pull_request_state, acquired.changes_requested, acquired.additions, acquired.deletions, acquired.changed_files, acquired.refreshed_at, acquired.stale_at, acquired.created_at, acquired.updated_at, acquired.git_branch, acquired.git_remote_origin, acquired.pull_request_title, acquired.pull_request_draft,
c.owner_id
FROM
acquired
@@ -3081,6 +3081,8 @@ type AcquireStaleChatDiffStatusesRow struct {
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
GitBranch string `db:"git_branch" json:"git_branch"`
GitRemoteOrigin string `db:"git_remote_origin" json:"git_remote_origin"`
PullRequestTitle string `db:"pull_request_title" json:"pull_request_title"`
PullRequestDraft bool `db:"pull_request_draft" json:"pull_request_draft"`
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
}
@@ -3107,6 +3109,8 @@ func (q *sqlQuerier) AcquireStaleChatDiffStatuses(ctx context.Context, limitVal
&i.UpdatedAt,
&i.GitBranch,
&i.GitRemoteOrigin,
&i.PullRequestTitle,
&i.PullRequestDraft,
&i.OwnerID,
); err != nil {
return nil, err
@@ -3266,7 +3270,7 @@ func (q *sqlQuerier) GetChatByIDForUpdate(ctx context.Context, id uuid.UUID) (Ch
const getChatDiffStatusByChatID = `-- name: GetChatDiffStatusByChatID :one
SELECT
chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin
chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin, pull_request_title, pull_request_draft
FROM
chat_diff_statuses
WHERE
@@ -3290,13 +3294,15 @@ func (q *sqlQuerier) GetChatDiffStatusByChatID(ctx context.Context, chatID uuid.
&i.UpdatedAt,
&i.GitBranch,
&i.GitRemoteOrigin,
&i.PullRequestTitle,
&i.PullRequestDraft,
)
return i, err
}
const getChatDiffStatusesByChatIDs = `-- name: GetChatDiffStatusesByChatIDs :many
SELECT
chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin
chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin, pull_request_title, pull_request_draft
FROM
chat_diff_statuses
WHERE
@@ -3326,6 +3332,8 @@ func (q *sqlQuerier) GetChatDiffStatusesByChatIDs(ctx context.Context, chatIds [
&i.UpdatedAt,
&i.GitBranch,
&i.GitRemoteOrigin,
&i.PullRequestTitle,
&i.PullRequestDraft,
); err != nil {
return nil, err
}
@@ -4272,6 +4280,8 @@ INSERT INTO chat_diff_statuses (
chat_id,
url,
pull_request_state,
pull_request_title,
pull_request_draft,
changes_requested,
additions,
deletions,
@@ -4282,17 +4292,21 @@ INSERT INTO chat_diff_statuses (
$1::uuid,
$2::text,
$3::text,
$4::boolean,
$5::integer,
$6::integer,
$4::text,
$5::boolean,
$6::boolean,
$7::integer,
$8::timestamptz,
$9::timestamptz
$8::integer,
$9::integer,
$10::timestamptz,
$11::timestamptz
)
ON CONFLICT (chat_id) DO UPDATE
SET
url = EXCLUDED.url,
pull_request_state = EXCLUDED.pull_request_state,
pull_request_title = EXCLUDED.pull_request_title,
pull_request_draft = EXCLUDED.pull_request_draft,
changes_requested = EXCLUDED.changes_requested,
additions = EXCLUDED.additions,
deletions = EXCLUDED.deletions,
@@ -4301,13 +4315,15 @@ SET
stale_at = EXCLUDED.stale_at,
updated_at = NOW()
RETURNING
chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin
chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin, pull_request_title, pull_request_draft
`
type UpsertChatDiffStatusParams struct {
ChatID uuid.UUID `db:"chat_id" json:"chat_id"`
Url sql.NullString `db:"url" json:"url"`
PullRequestState sql.NullString `db:"pull_request_state" json:"pull_request_state"`
PullRequestTitle string `db:"pull_request_title" json:"pull_request_title"`
PullRequestDraft bool `db:"pull_request_draft" json:"pull_request_draft"`
ChangesRequested bool `db:"changes_requested" json:"changes_requested"`
Additions int32 `db:"additions" json:"additions"`
Deletions int32 `db:"deletions" json:"deletions"`
@@ -4321,6 +4337,8 @@ func (q *sqlQuerier) UpsertChatDiffStatus(ctx context.Context, arg UpsertChatDif
arg.ChatID,
arg.Url,
arg.PullRequestState,
arg.PullRequestTitle,
arg.PullRequestDraft,
arg.ChangesRequested,
arg.Additions,
arg.Deletions,
@@ -4343,6 +4361,8 @@ func (q *sqlQuerier) UpsertChatDiffStatus(ctx context.Context, arg UpsertChatDif
&i.UpdatedAt,
&i.GitBranch,
&i.GitRemoteOrigin,
&i.PullRequestTitle,
&i.PullRequestDraft,
)
return i, err
}
@@ -4378,7 +4398,7 @@ SET
stale_at = EXCLUDED.stale_at,
updated_at = NOW()
RETURNING
chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin
chat_id, url, pull_request_state, changes_requested, additions, deletions, changed_files, refreshed_at, stale_at, created_at, updated_at, git_branch, git_remote_origin, pull_request_title, pull_request_draft
`
type UpsertChatDiffStatusReferenceParams struct {
@@ -4412,6 +4432,8 @@ func (q *sqlQuerier) UpsertChatDiffStatusReference(ctx context.Context, arg Upse
&i.UpdatedAt,
&i.GitBranch,
&i.GitRemoteOrigin,
&i.PullRequestTitle,
&i.PullRequestDraft,
)
return i, err
}
+6
View File
@@ -378,6 +378,8 @@ INSERT INTO chat_diff_statuses (
chat_id,
url,
pull_request_state,
pull_request_title,
pull_request_draft,
changes_requested,
additions,
deletions,
@@ -388,6 +390,8 @@ INSERT INTO chat_diff_statuses (
@chat_id::uuid,
sqlc.narg('url')::text,
sqlc.narg('pull_request_state')::text,
@pull_request_title::text,
@pull_request_draft::boolean,
@changes_requested::boolean,
@additions::integer,
@deletions::integer,
@@ -399,6 +403,8 @@ ON CONFLICT (chat_id) DO UPDATE
SET
url = EXCLUDED.url,
pull_request_state = EXCLUDED.pull_request_state,
pull_request_title = EXCLUDED.pull_request_title,
pull_request_draft = EXCLUDED.pull_request_draft,
changes_requested = EXCLUDED.changes_requested,
additions = EXCLUDED.additions,
deletions = EXCLUDED.deletions,