Skip to content

Commit 17707d8

Browse files
authored
Merge branch 'main' into agents/codex-cross-app-continuation
2 parents a2f72da + 02265cd commit 17707d8

11 files changed

Lines changed: 305 additions & 31 deletions

File tree

‎src/vs/sessions/contrib/chat/browser/sessionChatInputToolbar.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export function buildSessionPullRequestSections(pullRequests: readonly IResolved
123123
})],
124124
...getChatPillResourceLocation(ref.uri, label),
125125
ariaDescription: localize('sessionChatPills.pullRequestDescription', "{0}. {1}", stateDescription, ref.uri.toString(true)),
126+
...(!pullRequest && ref.title ? { tooltip: `${label}\n${ref.uri.toString(true)}` } : {}),
126127
...(pullRequest ? {
127128
pillHover: {
128129
element: () => createPullRequestHoverElement({
@@ -153,8 +154,9 @@ interface IResolvedSessionIssue {
153154
/** Builds Agents Window issue pill entries, enriching them when live details are available. */
154155
export function buildSessionIssueSections(issues: readonly IResolvedSessionIssue[], session: IActiveSession | undefined, commandService: ICommandService, clipboardService: IClipboardService, openerService: IOpenerService, sessionsService: ISessionsService): readonly IChatPillSection[] {
155156
const entries = issues.map(({ ref, issue }) => {
156-
const label = issue?.title
157-
? localize('sessionChatPills.issueWithTitle', "Issue #{0}: {1}", ref.number, issue.title)
157+
const title = issue?.title ?? ref.title;
158+
const label = title
159+
? localize('sessionChatPills.issueWithTitle', "Issue #{0}: {1}", ref.number, title)
158160
: localize('sessionChatPills.issue', "Issue #{0}", ref.number);
159161
return {
160162
id: ref.uri.toString(),
@@ -168,6 +170,7 @@ export function buildSessionIssueSections(issues: readonly IResolvedSessionIssue
168170
run: () => clipboardService.writeText(ref.uri.toString(true)),
169171
})],
170172
...getChatPillResourceLocation(ref.uri, label),
173+
...(!issue && ref.title ? { tooltip: `${label}\n${ref.uri.toString(true)}` } : {}),
171174
...(issue ? {
172175
pillHover: {
173176
element: () => createIssueHoverElement({

‎src/vs/sessions/contrib/chat/test/browser/sessionChatInputToolbar.test.ts‎

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,13 @@ suite('SessionChatInputToolbar', () => {
169169
}
170170

171171
test('adds rich GitHub hovers only when live details are available', async () => {
172-
const commandService = upcastPartial<ICommandService>({ executeCommand: async () => undefined });
172+
const commands: { readonly id: string; readonly args: readonly unknown[] }[] = [];
173+
const commandService = upcastPartial<ICommandService>({
174+
executeCommand: async (id, ...args) => {
175+
commands.push({ id, args });
176+
return undefined;
177+
},
178+
});
173179
const clipboardService = upcastPartial<IClipboardService>({ writeText: async () => { } });
174180
const openerService = upcastPartial<IOpenerService>({ open: async () => true });
175181
const sessionsService = upcastPartial<ISessionsService>({ setActive: () => { } });
@@ -178,6 +184,7 @@ suite('SessionChatInputToolbar', () => {
178184
repo: 'vscode',
179185
number: 332982,
180186
uri: URI.parse('https://github.com/microsoft/vscode/pull/332982'),
187+
title: 'Recorded pull request title',
181188
};
182189
const pullRequest: IGitHubPullRequest = {
183190
number: pullRequestRef.number,
@@ -200,6 +207,7 @@ suite('SessionChatInputToolbar', () => {
200207
repo: 'vscode',
201208
number: 42,
202209
uri: URI.parse('https://github.com/microsoft/vscode/issues/42'),
210+
title: 'Recorded issue title',
203211
};
204212
const issue: IGitHubIssue = {
205213
number: issueRef.number,
@@ -253,38 +261,67 @@ suite('SessionChatInputToolbar', () => {
253261
};
254262
const pullRequestHover = await renderHover(pullRequestEntry);
255263
const issueHover = await renderHover(issueEntry);
264+
pullRequestEntry?.open();
265+
unresolvedIssueEntry?.open();
256266

257267
assert.deepStrictEqual({
258268
pullRequest: {
269+
label: pullRequestEntry?.label,
259270
className: pullRequestHover?.className,
260271
repository: pullRequestHover?.querySelector('.sessions-pr-hover-repository')?.textContent,
261272
title: pullRequestHover?.querySelector('.sessions-pr-hover-title')?.textContent,
262273
description: pullRequestHover?.querySelector('.sessions-pr-hover-description-content')?.textContent,
263274
branches: [...pullRequestHover?.querySelectorAll('.sessions-pr-hover-branch') ?? []].map(element => element.textContent),
275+
unresolvedLabel: unresolvedPullRequestEntry?.label,
276+
unresolvedAriaLabel: unresolvedPullRequestEntry?.ariaLabel,
277+
unresolvedTooltip: unresolvedPullRequestEntry?.tooltip,
264278
unresolvedHover: unresolvedPullRequestEntry?.pillHover,
265279
},
266280
issue: {
281+
label: issueEntry?.label,
267282
className: issueHover?.className,
268283
repository: issueHover?.querySelector('.sessions-issue-hover-repository')?.textContent,
269284
title: issueHover?.querySelector('.sessions-issue-hover-title')?.textContent,
270285
description: issueHover?.querySelector('.sessions-issue-hover-description-content')?.textContent,
286+
unresolvedLabel: unresolvedIssueEntry?.label,
287+
unresolvedAriaLabel: unresolvedIssueEntry?.ariaLabel,
288+
unresolvedTooltip: unresolvedIssueEntry?.tooltip,
271289
unresolvedHover: unresolvedIssueEntry?.pillHover,
290+
openCommands: commands,
272291
},
273292
}, {
274293
pullRequest: {
294+
label: 'Pull Request #332982: Restore rich pill hovers',
275295
className: 'sessions-pr-hover',
276296
repository: 'microsoft/vscode',
277297
title: 'Restore rich pill hovers',
278298
description: 'Provides detailed pull request context.',
279299
branches: ['main', 'feature/rich-hover'],
300+
unresolvedLabel: 'Pull Request #332982: Recorded pull request title',
301+
unresolvedAriaLabel: 'Open Pull Request #332982: Recorded pull request title',
302+
unresolvedTooltip: 'Pull Request #332982: Recorded pull request title\nhttps://github.com/microsoft/vscode/pull/332982',
280303
unresolvedHover: undefined,
281304
},
282305
issue: {
306+
label: 'Issue #42: Rich issue hover',
283307
className: 'sessions-issue-hover',
284308
repository: 'microsoft/vscode#42',
285309
title: 'Rich issue hover',
286310
description: 'Provides detailed issue context.',
311+
unresolvedLabel: 'Issue #42: Recorded issue title',
312+
unresolvedAriaLabel: 'Open Issue #42: Recorded issue title',
313+
unresolvedTooltip: 'Issue #42: Recorded issue title\nhttps://github.com/microsoft/vscode/issues/42',
287314
unresolvedHover: undefined,
315+
openCommands: [
316+
{
317+
id: 'workbench.agentSessions.action.openPullRequest',
318+
args: [{ pullRequest: pullRequestRef }],
319+
},
320+
{
321+
id: 'workbench.agentSessions.action.openIssue',
322+
args: [{ issue: issueRef }],
323+
},
324+
],
288325
},
289326
});
290327
});

‎src/vs/sessions/contrib/providers/agentHost/browser/agentHostSessionArtifacts.ts‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ export interface ISessionArtifactPartition {
6969
readonly pullRequestTitles: ReadonlyMap<string, string>;
7070
/** Issues this session produced, most recent first. */
7171
readonly issueUrls: readonly string[];
72+
/** Titles the agent recorded for its issue artifacts, keyed by {@link linkKey}. */
73+
readonly issueTitles: ReadonlyMap<string, string>;
7274
}
7375

7476
interface ISessionArtifactEntry {
@@ -98,6 +100,7 @@ export function partitionSessionArtifacts(meta: SessionMeta | undefined): ISessi
98100
const pullRequestUrls: string[] = [];
99101
const pullRequestTitles = new Map<string, string>();
100102
const issueUrls: string[] = [];
103+
const issueTitles = new Map<string, string>();
101104

102105
for (const artifact of readSessionArtifacts(meta)) {
103106
const mapped = toSessionArtifact(artifact);
@@ -110,17 +113,17 @@ export function partitionSessionArtifacts(meta: SessionMeta | undefined): ISessi
110113
continue;
111114
}
112115

116+
const titles = artifact.type === SessionArtifactType.Issue ? issueTitles : pullRequestTitles;
117+
const key = linkKey(link);
118+
if (mapped.label && !titles.has(key)) {
119+
titles.set(key, mapped.label);
120+
}
121+
113122
if (artifact.type === SessionArtifactType.Issue) {
114123
issueUrls.push(link);
115124
continue;
116125
}
117126

118-
// The label an agent records for a pull request is its title; keep the
119-
// first one so a later duplicate cannot rewrite it.
120-
const key = linkKey(link);
121-
if (mapped.label && !pullRequestTitles.has(key)) {
122-
pullRequestTitles.set(key, mapped.label);
123-
}
124127
pullRequestUrls.push(link);
125128
}
126129

@@ -129,7 +132,7 @@ export function partitionSessionArtifacts(meta: SessionMeta | undefined): ISessi
129132
pullRequestUrls.reverse();
130133
issueUrls.reverse();
131134

132-
return { entries, pullRequestUrls, pullRequestTitles, issueUrls };
135+
return { entries, pullRequestUrls, pullRequestTitles, issueUrls, issueTitles };
133136
}
134137

135138
/** Case-insensitive de-duplication that keeps the first occurrence's casing. */

‎src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,12 @@ function isGitHubInfoEqual(a: IGitHubInfo | undefined, b: IGitHubInfo | undefine
339339
a.pullRequest?.title === b.pullRequest?.title &&
340340
a.pullRequest?.baseRefOid === b.pullRequest?.baseRefOid &&
341341
a.pullRequest?.headRefOid === b.pullRequest?.headRefOid &&
342-
arrayEquals(a.issues ?? [], b.issues ?? [], (x, y) => x.owner === y.owner && x.repo === y.repo && x.number === y.number);
342+
arrayEquals(a.issues ?? [], b.issues ?? [], (x, y) =>
343+
x.owner === y.owner &&
344+
x.repo === y.repo &&
345+
x.number === y.number &&
346+
isEqual(x.uri, y.uri) &&
347+
x.title === y.title);
343348
}
344349

345350
function dateEquals(a: Date | undefined, b: Date | undefined): boolean {
@@ -351,12 +356,17 @@ function markdownStringEquals(a: IMarkdownString | undefined, b: IMarkdownString
351356
}
352357

353358
/** Maps the GitHub issue URLs recorded on the session's metadata to issue references. */
354-
function toGitHubIssueRefs(issueUrls: readonly string[] | undefined): readonly IGitHubIssueRef[] | undefined {
359+
function toGitHubIssueRefs(issueUrls: readonly string[] | undefined, titles: ReadonlyMap<string, string>): readonly IGitHubIssueRef[] | undefined {
355360
const refs: IGitHubIssueRef[] = [];
356361
for (const url of issueUrls ?? []) {
357362
const reference = parseGitHubIssueUrl(url);
358363
if (reference) {
359-
refs.push({ ...reference, uri: URI.parse(url) });
364+
const title = titles.get(linkKey(url));
365+
refs.push({
366+
...reference,
367+
uri: URI.parse(url),
368+
...(title ? { title } : {}),
369+
});
360370
}
361371
}
362372
return refs.length > 0 ? refs : undefined;
@@ -390,7 +400,7 @@ function toGitHubPullRequestRefs(state: ISessionGitHubState | undefined, pullReq
390400
function toGitHubInfo(meta: SessionMeta | undefined): IGitHubInfo | undefined {
391401
const state = readSessionGitHubState(meta);
392402
const gitState = readSessionGitState(meta);
393-
const { pullRequestUrls, pullRequestTitles, issueUrls } = partitionSessionArtifacts(meta);
403+
const { pullRequestUrls, pullRequestTitles, issueUrls, issueTitles } = partitionSessionArtifacts(meta);
394404

395405
// Recorded pull requests lead discovered ones, so the first is the newest.
396406
const allPullRequests = toGitHubPullRequestRefs(state, dedupeLinks(pullRequestUrls, getSessionRelatedPullRequestUrls(state)), pullRequestTitles);
@@ -411,7 +421,7 @@ function toGitHubInfo(meta: SessionMeta | undefined): IGitHubInfo | undefined {
411421

412422
const pullRequests = allPullRequests?.filter(belongsToRepository);
413423
const pullRequest = pullRequests?.at(0);
414-
const issues = toGitHubIssueRefs(dedupeLinks(issueUrls))?.filter(belongsToRepository);
424+
const issues = toGitHubIssueRefs(dedupeLinks(issueUrls), issueTitles)?.filter(belongsToRepository);
415425

416426
return {
417427
owner: repository.owner,

‎src/vs/sessions/contrib/providers/agentHost/test/browser/localAgentHostSessionsProvider.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7891,7 +7891,7 @@ suite('LocalAgentHostSessionsProvider', () => {
78917891
{ id: 'a1', type: SessionArtifactType.PullRequest, label: 'Created', isArtifact: true, link: 'https://github.com/owner/repo/pull/50', isGitHub: true },
78927892
{ id: 'a2', type: SessionArtifactType.PullRequest, label: 'Referenced', isArtifact: false, link: 'https://github.com/owner/repo/pull/60', isGitHub: true },
78937893
{ id: 'a3', type: SessionArtifactType.PullRequest, label: 'Duplicate', isArtifact: true, link: 'https://github.com/OWNER/REPO/pull/41/', isGitHub: true },
7894-
{ id: 'a4', type: SessionArtifactType.Issue, label: 'Issue', isArtifact: true, link: 'https://github.com/owner/repo/issues/7', isGitHub: true },
7894+
{ id: 'a4', type: SessionArtifactType.Issue, label: 'Preserve promoted issue titles', isArtifact: true, link: 'https://github.com/owner/repo/issues/7', isGitHub: true },
78957895
{ id: 'a5', type: SessionArtifactType.PullRequest, label: 'Elsewhere', isArtifact: true, link: 'https://gitlab.com/owner/repo/-/merge_requests/3', isGitHub: false },
78967896
{ id: 'a6', type: SessionArtifactType.File, label: 'Plan', isArtifact: true, uri: 'file:///repo/plan.md' },
78977897
{ id: 'a7', type: SessionArtifactType.Issue, label: 'Referenced issue', isArtifact: false, link: 'https://github.com/owner/repo/issues/8', isGitHub: true },
@@ -7909,13 +7909,13 @@ suite('LocalAgentHostSessionsProvider', () => {
79097909
assert.deepStrictEqual({
79107910
activePullRequest: gitHubInfo?.pullRequest?.number,
79117911
pullRequests: gitHubInfo?.pullRequests?.map(pullRequest => pullRequest.number),
7912-
issues: gitHubInfo?.issues?.map(issue => issue.number),
7912+
issues: gitHubInfo?.issues?.map(issue => [issue.number, issue.title]),
79137913
artifacts: session.artifacts?.get().map(artifact => [artifact.id, artifact.isArtifact]),
79147914
}, {
79157915
activePullRequest: 41,
79167916
pullRequests: [41, 50, 42],
79177917
// Only issues the session produced are polled; a referenced one stays a reference.
7918-
issues: [7],
7918+
issues: [[7, 'Preserve promoted issue titles']],
79197919
artifacts: [
79207920
['a8', false],
79217921
['a7', false],

‎src/vs/sessions/services/sessions/common/session.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,8 @@ export interface IGitHubIssueRef {
404404
readonly number: number;
405405
/** URI of the issue. */
406406
readonly uri: URI;
407+
/** Issue title recorded by the session, when known. */
408+
readonly title?: string;
407409
}
408410

409411
export interface ISessionChangesSummary {
@@ -1052,7 +1054,13 @@ export function gitHubInfoEqual(a: IGitHubInfo | undefined, b: IGitHubInfo | und
10521054
(aIcon === bIcon || (!!aIcon && !!bIcon && ThemeIcon.isEqual(aIcon, bIcon))) &&
10531055
a.pullRequest?.title === b.pullRequest?.title &&
10541056
a.pullRequest?.baseRefOid === b.pullRequest?.baseRefOid &&
1055-
a.pullRequest?.headRefOid === b.pullRequest?.headRefOid;
1057+
a.pullRequest?.headRefOid === b.pullRequest?.headRefOid &&
1058+
arrayEquals(a.issues ?? [], b.issues ?? [], (x, y) =>
1059+
x.owner === y.owner &&
1060+
x.repo === y.repo &&
1061+
x.number === y.number &&
1062+
isEqual(x.uri, y.uri) &&
1063+
x.title === y.title);
10561064
}
10571065

10581066
/**

‎src/vs/sessions/services/sessions/test/common/session.test.ts‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,23 @@ suite('sessionWorkspaceEqual', () => {
198198
assert.strictEqual(sessionWorkspaceEqual(workspace('main', constObservable(gitHubInfoA)), workspace('main', constObservable(gitHubInfoB))), true);
199199
});
200200

201+
test('compares recorded issue titles in GitHub info', () => {
202+
const uri = URI.parse('https://github.com/owner/repo/issues/42');
203+
const base: IGitHubInfo = {
204+
owner: 'owner',
205+
repo: 'repo',
206+
issues: [{ owner: 'owner', repo: 'repo', number: 42, uri, title: 'Recorded title' }],
207+
};
208+
209+
assert.deepStrictEqual({
210+
equivalent: sessionWorkspaceEqual(workspace('main', constObservable(base)), workspace('main', constObservable({ ...base, issues: [{ ...base.issues![0] }] }))),
211+
changedTitle: sessionWorkspaceEqual(workspace('main', constObservable(base)), workspace('main', constObservable({ ...base, issues: [{ ...base.issues![0], title: 'Updated title' }] }))),
212+
}, {
213+
equivalent: true,
214+
changedTitle: false,
215+
});
216+
});
217+
201218
test('returns false when folder repository metadata changes', () => {
202219
assert.strictEqual(sessionWorkspaceEqual(workspace('main'), workspace('feature')), false);
203220
});

‎src/vs/workbench/browser/chatDropdownPill.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ export class ChatDropdownPillActionViewItem extends ChatPillActionViewItem {
298298
getAriaLabel: item => item.label ?? '',
299299
getWidgetAriaLabel: () => this._pillOptions.title,
300300
},
301-
{ minWidth: 240, maxWidth: 460, widgetClassName: 'show-file-icons' },
301+
{ minWidth: 240, maxWidth: 460, widgetClassName: 'show-file-icons chat-pill-dropdown' },
302302
);
303303
}
304304

‎src/vs/workbench/browser/media/chatPills.css‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@
169169
background-position: center center;
170170
}
171171

172+
.action-widget.chat-pill-dropdown .monaco-list .monaco-list-row.has-toolbar:not(.has-standalone-toggle):not(.has-inline-toggle):not(.has-detail) {
173+
padding-right: 0;
174+
}
175+
176+
.action-widget.chat-pill-dropdown .monaco-list .monaco-list-row.has-toolbar:not(.has-standalone-toggle):not(.has-inline-toggle):not(.has-detail) .action-list-item-toolbar {
177+
margin-right: 0;
178+
}
179+
172180
/* Horizontally scrollable status pills above a chat input. */
173181
.chat-pills-row {
174182
width: 100%;

0 commit comments

Comments
 (0)