From 86ae3ca69ef5dd4656cbfd08dbeeeae6219782cc Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 2 Dec 2025 12:05:23 +1100 Subject: [PATCH] Display pending CLI requests in Agents view (#2314) --- .../copilotcli/node/copilotcliSession.ts | 8 ++++++- .../node/copilotcliSessionService.ts | 24 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/extensions/copilot/src/extension/agents/copilotcli/node/copilotcliSession.ts b/extensions/copilot/src/extension/agents/copilotcli/node/copilotcliSession.ts index 8af79c4315f..e511d7411ce 100644 --- a/extensions/copilot/src/extension/agents/copilotcli/node/copilotcliSession.ts +++ b/extensions/copilot/src/extension/agents/copilotcli/node/copilotcliSession.ts @@ -38,6 +38,7 @@ export interface ICopilotCLISession extends IDisposable { readonly isolationEnabled: boolean; readonly workingDirectory?: Uri; }; + readonly pendingPrompt: string | undefined; attachPermissionHandler(handler: PermissionHandler): IDisposable; attachStream(stream: vscode.ChatResponseStream): IDisposable; handleRequest( @@ -82,7 +83,10 @@ export class CopilotCLISession extends DisposableStore implements ICopilotCLISes }; } private _lastUsedModel: string | undefined; - + private _pendingPrompt: string | undefined; + public get pendingPrompt(): string | undefined { + return this._pendingPrompt; + } constructor( private readonly _options: CopilotCLISessionOptions, private readonly _sdkSession: Session, @@ -127,6 +131,7 @@ export class CopilotCLISession extends DisposableStore implements ICopilotCLISes if (this.isDisposed) { throw new Error('Session disposed'); } + this._pendingPrompt = prompt; this._status = ChatSessionStatus.InProgress; this._statusChange.fire(this._status); @@ -259,6 +264,7 @@ export class CopilotCLISession extends DisposableStore implements ICopilotCLISes this.logService.error(`[CopilotCLISession] Invoking session (error) ${this.sessionId}`, error); this._stream?.markdown(`\n\n❌ Error: ${error instanceof Error ? error.message : String(error)}`); } finally { + this._pendingPrompt = undefined; disposables.dispose(); } } diff --git a/extensions/copilot/src/extension/agents/copilotcli/node/copilotcliSessionService.ts b/extensions/copilot/src/extension/agents/copilotcli/node/copilotcliSessionService.ts index 50d2e60e754..0e016fc9ca5 100644 --- a/extensions/copilot/src/extension/agents/copilotcli/node/copilotcliSessionService.ts +++ b/extensions/copilot/src/extension/agents/copilotcli/node/copilotcliSessionService.ts @@ -153,6 +153,26 @@ export class CopilotCLISessionService extends Disposable implements ICopilotCLIS }) )); + const diskSessionIds = new Set(diskSessions.map(s => s.id)); + // If we have a new session that has started, then return that as well. + // Possible SDK has not yet persisted it to disk. + const newSessions = coalesce(Array.from(this._sessionWrappers.values()) + .filter(session => !diskSessionIds.has(session.object.sessionId)) + .filter(session => session.object.status === ChatSessionStatus.InProgress) + .map(session => { + const label = labelFromPrompt(session.object.pendingPrompt ?? ''); + if (!label) { + return; + } + + return { + id: session.object.sessionId, + label, + status: session.object.status, + timing: { startTime: Date.now() }, + } satisfies ICopilotCLISessionItem; + })); + // Merge with cached sessions (new sessions not yet persisted by SDK) const allSessions = diskSessions .map(session => { @@ -160,7 +180,7 @@ export class CopilotCLISessionService extends Disposable implements ICopilotCLIS ...session, status: this._sessionWrappers.get(session.id)?.object?.status } satisfies ICopilotCLISessionItem; - }); + }).concat(newSessions); return allSessions; } catch (error) { @@ -336,4 +356,4 @@ export class RefCountedSession extends RefCountedDisposable implements IReferenc dispose(): void { this.release(); } -} \ No newline at end of file +}