Skip to content

Commit 330fc12

Browse files
authored
Fixing session logs and styling polish (#7109)
Adopting the new api manager broke this
1 parent 0126bf1 commit 330fc12

6 files changed

Lines changed: 131 additions & 89 deletions

File tree

package.json

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,11 +1662,6 @@
16621662
"command": "codingAgent.openSessionLog",
16631663
"title": "Open Coding Agent Session Log",
16641664
"category": "%command.pull.request.category%"
1665-
},
1666-
{
1667-
"command": "sessionLog.openOnWeb",
1668-
"title": "Open on Web",
1669-
"icon": "$(globe)"
16701665
}
16711666
],
16721667
"viewsWelcome": [
@@ -2384,10 +2379,6 @@
23842379
{
23852380
"command": "review.copyPrLink",
23862381
"when": "github:inReviewMode"
2387-
},
2388-
{
2389-
"command": "sessionLog.openOnWeb",
2390-
"when": "false"
23912382
}
23922383
],
23932384
"view/title": [
@@ -2854,11 +2845,6 @@
28542845
"command": "pr.addFileComment",
28552846
"group": "navigation",
28562847
"when": "(resourceScheme == pr) || (resourcePath in github:viewedFiles) || (resourcePath in github:unviewedFiles)"
2857-
},
2858-
{
2859-
"command": "sessionLog.openOnWeb",
2860-
"group": "navigation",
2861-
"when": "activeWebviewPanelId == 'pr.codingAgentSessionLogView'"
28622848
}
28632849
],
28642850
"editor/content": [

src/github/copilotRemoteAgent.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,18 +324,18 @@ export class CopilotRemoteAgentManager extends Disposable {
324324
return await capi.getLogsFromZipUrl(lastRun.logs_url);
325325
}
326326

327-
async getSessionLogsFromPullRequest(pullRequestId: number): Promise<IAPISessionLogs> {
327+
async getMostRecentSessionLogsFromPullRequest(pullRequestId: number, completedOnly = true): Promise<IAPISessionLogs | undefined> {
328328
const capi = await this.copilotApi;
329329
if (!capi) {
330-
return { sessionId: '', logs: '' };
330+
return undefined;
331331
}
332332

333333
const sessions = await capi.getAllSessions(pullRequestId);
334-
const completedSessions = sessions.filter(s => s.state === 'completed');
335-
if (completedSessions.length === 0) {
336-
return { sessionId: '', logs: '' };
334+
const mostRecentSession = sessions.filter(s => !completedOnly || s.state === 'completed').at(0);
335+
if (!mostRecentSession) {
336+
return undefined;
337337
}
338-
const mostRecentSession = this.getLatestRun(completedSessions);
338+
339339
const logs = await capi.getLogsFromSession(mostRecentSession.id);
340340
return { sessionId: mostRecentSession.id, logs };
341341
}

src/lm/tools/activePullRequestTool.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,12 @@ export class ActivePullRequestTool implements vscode.LanguageModelTool<FetchIssu
9898
): Promise<string | string[]> {
9999
let copilotSteps: string | string[] = [];
100100
try {
101-
const logsResponseText = await this.copilotRemoteAgentManager.getSessionLogsFromPullRequest(pullRequest.id);
102-
copilotSteps = this.parseCopilotEventStream(logsResponseText.logs);
101+
const logs = await this.copilotRemoteAgentManager.getMostRecentSessionLogsFromPullRequest(pullRequest.id);
102+
if (!logs) {
103+
throw new Error('Could not get session logs');
104+
}
105+
106+
copilotSteps = this.parseCopilotEventStream(logs.logs);
103107
if (copilotSteps.length === 0) {
104108
throw new Error('Empty Copilot agent logs received');
105109
}

src/view/sessionLogView.ts

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,6 @@ export class SessionLogViewManager extends Disposable implements vscode.WebviewP
7070

7171
return this.open(sessionLogs, undefined);
7272
}));
73-
74-
this._register(vscode.commands.registerCommand('sessionLog.openOnWeb', async () => {
75-
if (!this._activePanel) {
76-
return;
77-
}
78-
79-
const pullInfo = this._activePanel.pullInfo;
80-
if (!pullInfo) {
81-
vscode.window.showErrorMessage(vscode.l10n.t('No pull for this session.'));
82-
return;
83-
}
84-
85-
86-
const sessionUrl = vscode.Uri.parse(`https://${pullInfo.host}/${pullInfo.owner}/${pullInfo.repo}/pull/${pullInfo.pullId}/agent-sessions/${this._activePanel.sessionId}`);
87-
return vscode.env.openExternal(sessionUrl);
88-
}));
8973
}
9074

9175
public override dispose() {
@@ -100,12 +84,18 @@ export class SessionLogViewManager extends Disposable implements vscode.WebviewP
10084

10185
async openForPull(pullRequest: PullRequestModel): Promise<void> {
10286
try {
103-
const sessionLogs = await this.copilotAgentManager.getSessionLogsFromPullRequest(pullRequest.id);
87+
const sessionLogs = await this.copilotAgentManager.getMostRecentSessionLogsFromPullRequest(pullRequest.id, false);
10488
if (!sessionLogs) {
10589
throw new Error('No sessions found for this pull request.');
10690
}
10791

108-
return this.open(sessionLogs, pullRequest);
92+
const existingPanel = this.getPanelForPullRequest(pullRequest);
93+
if (existingPanel) {
94+
existingPanel.revealAndRefresh(sessionLogs);
95+
return;
96+
} else {
97+
return this.open(sessionLogs, pullRequest);
98+
}
10999
} catch (error) {
110100
Logger.error(`Failed to retrieve session logs: ${error}`, 'SessionLogViewManager');
111101
const url = await this.copilotAgentManager.getSessionUrlFromPullRequest(pullRequest.id);
@@ -117,6 +107,10 @@ export class SessionLogViewManager extends Disposable implements vscode.WebviewP
117107
}
118108
}
119109

110+
private getPanelForPullRequest(pullRequest: PullRequestModel): SessionLogView | undefined {
111+
return Array.from(this._panels).find(panel => panel.view.isForPullRequest(pullRequest))?.view;
112+
}
113+
120114
async open(logs: IAPISessionLogs, pullRequest: PullRequestModel | undefined): Promise<void> {
121115
const copilotApi = await getCopilotApi(this.credentialStore);
122116
if (!copilotApi) {
@@ -133,14 +127,7 @@ export class SessionLogViewManager extends Disposable implements vscode.WebviewP
133127
}
134128
);
135129

136-
const pullInfo: SessionPullInfo & { title: string } | undefined = pullRequest ? {
137-
host: pullRequest.githubRepository.remote.gitProtocol.host,
138-
owner: pullRequest.githubRepository.remote.owner,
139-
repo: pullRequest.githubRepository.remote.repositoryName,
140-
pullId: pullRequest.number,
141-
title: pullRequest.title,
142-
} : undefined;
143-
130+
const pullInfo = pullRequest ? toPullInfo(pullRequest) : undefined;
144131
return this.setupWebview(webviewPanel, logs.sessionId, pullInfo, copilotApi);
145132
}
146133

@@ -160,29 +147,6 @@ export class SessionLogViewManager extends Disposable implements vscode.WebviewP
160147
}
161148

162149
private async setupWebview(webviewPanel: vscode.WebviewPanel, sessionId: string, pullInfo: SessionPullInfo & { title: string } | undefined, copilotApi: CopilotApi): Promise<void> {
163-
const distDir = vscode.Uri.joinPath(this.context.extensionUri, 'dist');
164-
165-
webviewPanel.webview.options = {
166-
enableScripts: true,
167-
localResourceRoots: [
168-
distDir
169-
]
170-
};
171-
webviewPanel.webview.html = `<!DOCTYPE html>
172-
<html lang="en">
173-
<head>
174-
<meta charset="UTF-8">
175-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
176-
<title>${vscode.l10n.t('Session Log')}</title>
177-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline' ${webviewPanel.webview.cspSource}; script-src ${webviewPanel.webview.cspSource} 'unsafe-eval'; font-src ${webviewPanel.webview.cspSource};">
178-
</head>
179-
<body>
180-
<div id="app"></div>
181-
182-
<script type="module" src="${webviewPanel.webview.asWebviewUri(vscode.Uri.joinPath(distDir, 'webview-session-log-view.js'))}"></script>
183-
</body>
184-
</html>`;
185-
186150
const logView = new SessionLogView(sessionId, pullInfo, webviewPanel, copilotApi, this.context, this.reposManagers, this.telemetry);
187151
const panelDisposables: vscode.Disposable[] = [];
188152
const panelEntry = { view: logView, disposables: panelDisposables };
@@ -213,7 +177,7 @@ class SessionLogView extends Disposable {
213177
public readonly pullInfo: SessionPullInfo & { title: string } | undefined,
214178
private readonly webviewPanel: vscode.WebviewPanel,
215179
private readonly copilotApi: CopilotApi,
216-
context: vscode.ExtensionContext,
180+
private readonly context: vscode.ExtensionContext,
217181
reposManagers: RepositoriesManager,
218182
telemetry: ITelemetry,
219183
) {
@@ -235,7 +199,6 @@ class SessionLogView extends Disposable {
235199

236200
this._register(this.webviewPanel.webview.onDidReceiveMessage(async (message: any) => {
237201
if (message.type === 'openPullRequestView') {
238-
239202
let pullRequest: PullRequestModel | undefined;
240203
if (pullInfo) {
241204
const folderManager = reposManagers.getManagerForRepository(pullInfo.owner, pullInfo.repo) ?? reposManagers.folderManagers.at(0);
@@ -249,6 +212,14 @@ class SessionLogView extends Disposable {
249212

250213
const folderManager = reposManagers.getManagerForIssueModel(pullRequest) ?? reposManagers.folderManagers[0];
251214
await PullRequestOverviewPanel.createOrShow(telemetry, context.extensionUri, folderManager, pullRequest);
215+
} else if (message.type === 'openOnWeb') {
216+
if (!pullInfo) {
217+
vscode.window.showErrorMessage(vscode.l10n.t('No pull request information available for this session.'));
218+
return;
219+
}
220+
221+
const sessionUrl = vscode.Uri.parse(`https://${pullInfo.host}/${pullInfo.owner}/${pullInfo.repo}/pull/${pullInfo.pullId}/agent-sessions/${this.sessionId}`);
222+
return vscode.env.openExternal(sessionUrl);
252223
}
253224
}));
254225

@@ -265,7 +236,45 @@ class SessionLogView extends Disposable {
265236
super.dispose();
266237
}
267238

239+
public isForPullRequest(pullRequest: PullRequestModel): boolean {
240+
if (!this.pullInfo) {
241+
return false;
242+
}
243+
const inInfo = toPullInfo(pullRequest);
244+
return arePullInfosEqual(this.pullInfo, inInfo);
245+
}
246+
247+
public revealAndRefresh(_sessionLogs: IAPISessionLogs) {
248+
// TODO: handle opening different session from pull
249+
this.initialize();
250+
this.webviewPanel.reveal();
251+
}
252+
268253
private async initialize() {
254+
const distDir = vscode.Uri.joinPath(this.context.extensionUri, 'dist');
255+
256+
this.webviewPanel.webview.options = {
257+
enableScripts: true,
258+
localResourceRoots: [
259+
distDir
260+
]
261+
};
262+
this.webviewPanel.webview.html = `<!DOCTYPE html>
263+
<html lang="en">
264+
<head>
265+
<meta charset="UTF-8">
266+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
267+
<title>${vscode.l10n.t('Session Log')}</title>
268+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline' ${this.webviewPanel.webview.cspSource}; script-src ${this.webviewPanel.webview.cspSource} 'unsafe-eval'; font-src ${this.webviewPanel.webview.cspSource};">
269+
</head>
270+
<body>
271+
<div id="app"></div>
272+
273+
<script type="module" src="${this.webviewPanel.webview.asWebviewUri(vscode.Uri.joinPath(distDir, 'webview-session-log-view.js'))}"></script>
274+
</body>
275+
</html>`;
276+
277+
269278
let readyResolve: (value: void | PromiseLike<void>) => void;
270279
const ready = new Promise<void>(resolve => { readyResolve = resolve; });
271280
this._register(this.webviewPanel.webview.onDidReceiveMessage((message: any) => {
@@ -289,7 +298,6 @@ class SessionLogView extends Disposable {
289298
return;
290299
}
291300

292-
293301
this.webviewPanel.webview.postMessage({
294302
type: 'init',
295303
themeData,
@@ -333,6 +341,23 @@ class SessionLogView extends Disposable {
333341
}
334342
}
335343

344+
function toPullInfo(pullRequest: PullRequestModel): SessionPullInfo & { title: string; } {
345+
return {
346+
host: pullRequest.githubRepository.remote.gitProtocol.host,
347+
owner: pullRequest.githubRepository.remote.owner,
348+
repo: pullRequest.githubRepository.remote.repositoryName,
349+
pullId: pullRequest.number,
350+
title: pullRequest.title,
351+
};
352+
}
353+
354+
function arePullInfosEqual(a: SessionPullInfo, b: SessionPullInfo): boolean {
355+
return a.host === b.host &&
356+
a.owner === b.owner &&
357+
a.repo === b.repo &&
358+
a.pullId === b.pullId;
359+
}
360+
336361
async function loadCurrentThemeData(): Promise<any> {
337362
let themeData: any = null;
338363
const currentThemeName = vscode.workspace.getConfiguration('workbench').get<string>('colorTheme');

webviews/sessionLogView/index.css

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@
2424
margin: 10px 0;
2525
padding-bottom: 8px;
2626
border-bottom: 1px solid var(--vscode-sideBarSectionHeader-border);
27+
28+
.session-label {
29+
padding-bottom: 0.2em;
30+
}
31+
32+
.session-value {
33+
color: var(--vscode-descriptionForeground);
34+
}
2735
}
2836

2937
.session-header-title {
@@ -33,7 +41,20 @@
3341

3442
h1,
3543
h2 {
36-
margin: 0.4em 0;
44+
margin: 0a.4em 0;
45+
}
46+
47+
h2 a {
48+
color: inherit;
49+
50+
.pull-request-id {
51+
color: var(--vscode-textLink-foreground);
52+
}
53+
}
54+
55+
nav ul {
56+
list-style: none;
57+
padding: 0;
3758
}
3859
}
3960

webviews/sessionLogView/sessionView.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,29 @@ const SessionHeader: React.FC<SessionHeaderProps> = ({ info, pullInfo }) => {
4343

4444
return (
4545
<header className="session-header">
46-
{pullInfo && (
47-
<div className='session-header-title'>
48-
<button
49-
className="session-pull-button"
50-
onClick={() => {
51-
vscode.postMessage({ type: 'openPullRequestView' });
52-
}}>
53-
<span className="icon"><i className={'codicon codicon-chevron-left'}></i></span>
54-
Back to Pull Request
55-
</button>
46+
<div className='session-header-title'>
47+
<h1>Coding Agent Session Log</h1>
5648

57-
<h1>Coding Agent Session Log</h1>
49+
{pullInfo && <>
5850
<h2>
59-
<span className="icon"><i className={'codicon codicon-git-pull-request'}></i></span> {pullInfo.title}
51+
<a onClick={() => {
52+
vscode.postMessage({ type: 'openPullRequestView' });
53+
}} title='Back to pull request'>
54+
<span className="icon"><i className={'codicon codicon-git-pull-request'}></i></span> {pullInfo.title} <span className="pull-request-id">#{pullInfo.pullId}</span>
55+
</a>
6056
</h2>
61-
</div>
62-
)}
57+
58+
<nav>
59+
<ul>
60+
<li>
61+
<button onClick={() => {
62+
vscode.postMessage({ type: 'openOnWeb' });
63+
}}>Open on GitHub</button>
64+
</li>
65+
</ul>
66+
</nav>
67+
</>}
68+
</div>
6369

6470
<div className="session-header-info">
6571
<div className="session-status">

0 commit comments

Comments
 (0)