Skip to content

Commit 6175b04

Browse files
committed
Implement setup stage progress display in session logs
1 parent 38d3c3d commit 6175b04

File tree

6 files changed

+127
-7
lines changed

6 files changed

+127
-7
lines changed

src/github/copilotRemoteAgent.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type RemoteAgentResult = RemoteAgentSuccessResult | RemoteAgentErrorResult;
2929
export interface IAPISessionLogs {
3030
readonly info: SessionInfo;
3131
readonly logs: string;
32+
readonly setupLogs?: string[];
3233
}
3334

3435
export interface ICopilotRemoteAgentCommandArgs {
@@ -629,7 +630,23 @@ export class CopilotRemoteAgentManager extends Disposable {
629630
}
630631

631632
const logs = await capi.getLogsFromSession(session.id);
632-
return { info: session, logs };
633+
634+
// If session is in progress, try to fetch setup logs from workflow
635+
let setupLogs: string[] | undefined;
636+
if (session.state === 'in_progress' || logs.trim().length === 0) {
637+
try {
638+
// Get the pull request model to access workflow logs
639+
const pullRequest = await this.findPullRequestForSession(pullRequestId);
640+
if (pullRequest) {
641+
setupLogs = await this.getSessionLogsFromAction(pullRequest);
642+
}
643+
} catch (error) {
644+
// If we can't fetch setup logs, don't fail the entire request
645+
Logger.warn(`Failed to fetch setup logs for session ${session.id}: ${error}`, CopilotRemoteAgentManager.ID);
646+
}
647+
}
648+
649+
return { info: session, logs, setupLogs };
633650
}
634651

635652
async getSessionUrlFromPullRequest(pullRequest: PullRequestModel): Promise<string | undefined> {
@@ -645,6 +662,28 @@ export class CopilotRemoteAgentManager extends Disposable {
645662
return sessions.html_url;
646663
}
647664

665+
private async findPullRequestForSession(pullRequestId: number): Promise<PullRequestModel | undefined> {
666+
// Search through all repository managers to find the pull request
667+
for (const manager of this.repositoriesManager.folderManagers) {
668+
if (manager.gitHubRepositories.length === 0) {
669+
continue;
670+
}
671+
672+
for (const githubRepo of manager.gitHubRepositories) {
673+
try {
674+
const pullRequest = await githubRepo.getPullRequest(pullRequestId);
675+
if (pullRequest) {
676+
return pullRequest;
677+
}
678+
} catch (error) {
679+
// Continue searching in other repositories
680+
continue;
681+
}
682+
}
683+
}
684+
return undefined;
685+
}
686+
648687
private getLatestRun<T extends { last_updated_at?: string; updated_at?: string }>(runs: T[]): T {
649688
return runs
650689
.slice()

src/view/sessionLogView.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,8 @@ class SessionLogView extends Disposable {
409409
type: 'loaded',
410410
pullInfo: this._source.type === 'pull' ? this._source.pullRequest : undefined,
411411
info: apiResponse.info,
412-
logs: apiResponse.logs
412+
logs: apiResponse.logs,
413+
setupLogs: apiResponse.setupLogs
413414
} as messages.LoadedMessage);
414415

415416
if (apiResponse.info.state === 'in_progress') {
@@ -430,7 +431,8 @@ class SessionLogView extends Disposable {
430431
type: 'update',
431432
pullInfo: this._source.type === 'pull' ? this._source.pullRequest : undefined,
432433
info: apiResult.info,
433-
logs: apiResult.logs
434+
logs: apiResult.logs,
435+
setupLogs: apiResult.setupLogs
434436
} as messages.UpdateMessage);
435437

436438
if (apiResult.info.state !== 'in_progress') {

webviews/sessionLogView/app.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const themeName = 'vscode-theme';
1616

1717
type SessionViewState =
1818
| { state: 'loading' }
19-
| { state: 'ready'; readonly info: SessionInfo; readonly logs: readonly SessionResponseLogChunk[]; readonly pullInfo: messages.PullInfo | undefined }
19+
| { state: 'ready'; readonly info: SessionInfo; readonly logs: readonly SessionResponseLogChunk[]; readonly pullInfo: messages.PullInfo | undefined; readonly setupLogs?: readonly string[] }
2020
| { state: 'error'; readonly url: string | undefined }
2121
;
2222

@@ -49,7 +49,8 @@ export function App() {
4949
state: 'ready',
5050
info: message.info,
5151
logs: parseSessionLogs(message.logs),
52-
pullInfo: message.pullInfo
52+
pullInfo: message.pullInfo,
53+
setupLogs: message.setupLogs
5354
});
5455
break;
5556
}
@@ -89,7 +90,7 @@ export function App() {
8990
</div>
9091
);
9192
} else {
92-
return <SessionView info={state.info} logs={state.logs} pullInfo={state.pullInfo} />;
93+
return <SessionView info={state.info} logs={state.logs} pullInfo={state.pullInfo} setupLogs={state.setupLogs} />;
9394
}
9495
}
9596

webviews/sessionLogView/index.css

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,41 @@ button.codeview-expand:hover {
185185
to {
186186
transform: rotate(360deg);
187187
}
188+
}
189+
190+
.setup-stage-log {
191+
margin: 1em 0;
192+
border: 1px solid var(--vscode-widget-border);
193+
border-radius: 4px;
194+
overflow: hidden;
195+
}
196+
197+
.setup-stage-title {
198+
display: flex;
199+
align-items: center;
200+
padding: 8px 12px;
201+
background: var(--vscode-sideBarSectionHeader-background);
202+
border-bottom: 1px solid var(--vscode-sideBarSectionHeader-border);
203+
color: var(--vscode-sideBarSectionHeader-foreground);
204+
font-size: 13px;
205+
font-weight: 600;
206+
margin: 0;
207+
gap: 8px;
208+
}
209+
210+
.setup-log-content {
211+
padding: 8px 12px;
212+
background: var(--vscode-notebook-cellEditorBackground);
213+
max-height: 200px;
214+
overflow-y: auto;
215+
}
216+
217+
.setup-log-line {
218+
font-family: var(--vscode-editor-font-family);
219+
font-size: var(--vscode-editor-font-size);
220+
line-height: 1.4;
221+
color: var(--vscode-foreground);
222+
margin: 2px 0;
223+
white-space: pre-wrap;
224+
word-wrap: break-word;
188225
}

webviews/sessionLogView/messages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ export interface LoadedMessage {
2020
pullInfo: PullInfo | undefined;
2121
info: SessionInfo;
2222
logs: string;
23+
setupLogs?: string[];
2324
}
2425

2526
export interface UpdateMessage {
2627
type: 'update';
2728
pullInfo: PullInfo | undefined;
2829
info: SessionInfo;
2930
logs: string;
31+
setupLogs?: string[];
3032
}
3133

3234
export interface ResetMessage {

webviews/sessionLogView/sessionView.tsx

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,25 @@ interface SessionViewProps {
1919
readonly pullInfo: PullInfo | undefined;
2020
readonly info: SessionInfo;
2121
readonly logs: readonly SessionResponseLogChunk[];
22+
readonly setupLogs?: readonly string[];
2223
}
2324

2425
export const SessionView: React.FC<SessionViewProps> = (props) => {
2526
return (
2627
<div className="session-container">
2728
<SessionHeader info={props.info} pullInfo={props.pullInfo} />
29+
{props.setupLogs && props.setupLogs.length > 0 && (
30+
<SetupStageLog setupLogs={props.setupLogs} />
31+
)}
2832
<SessionLog logs={props.logs} />
2933
{props.info.state === 'in_progress' && (
3034
<div className="session-in-progress-indicator">
3135
<span className="icon"><i className="codicon codicon-loading"></i></span>
32-
Session is in progress...</div>
36+
{props.logs.length === 0 && props.setupLogs && props.setupLogs.length > 0
37+
? 'Configuring environment...'
38+
: 'Session is in progress...'
39+
}
40+
</div>
3341
)}
3442
</div>
3543
);
@@ -268,3 +276,34 @@ function toFileLabel(file: string): string {
268276
const parts = file.split('/');
269277
return parts.slice(6).join('/');
270278
}
279+
280+
// Setup Stage Log component
281+
interface SetupStageLogProps {
282+
readonly setupLogs: readonly string[];
283+
}
284+
285+
const SetupStageLog: React.FC<SetupStageLogProps> = ({ setupLogs }) => {
286+
if (!setupLogs || setupLogs.length === 0) {
287+
return null;
288+
}
289+
290+
const setupSteps = setupLogs
291+
.filter(line => line.trim().length > 0)
292+
.map((line, index) => (
293+
<div key={index} className="setup-log-line">
294+
{line}
295+
</div>
296+
));
297+
298+
return (
299+
<div className="setup-stage-log">
300+
<h3 className="setup-stage-title">
301+
<span className="icon"><i className="codicon codicon-gear"></i></span>
302+
Environment Setup
303+
</h3>
304+
<div className="setup-log-content">
305+
{setupSteps}
306+
</div>
307+
</div>
308+
);
309+
};

0 commit comments

Comments
 (0)