From eb9f1aac71020bc5842b66299fbd25a2261a7e5c Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 21 Apr 2026 16:13:30 -0700 Subject: [PATCH 1/3] Handle things way better Co-authored-by: Copilot --- .../copilotcli/node/copilotcliSession.ts | 7 +++++++ .../node/test/copilotcliSession.spec.ts | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts b/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts index 96f64b577f37c..468f1b0e924f6 100644 --- a/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts +++ b/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts @@ -878,6 +878,13 @@ export class CopilotCLISession extends DisposableStore implements ICopilotCLISes this._stream?.progress(l10n.t('Compacting conversation...')); await this._sdkSession.initializeAndValidateTools(); this._sdkSession.currentMode = 'interactive'; + // Mirror the Copilot CLI SDK's own `messages.length < 2` guard to + // avoid its "Nothing to compact." throw after a prior compaction. + const messages = await this._sdkSession.getChatMessages(); + if (messages.length < 2) { + this._stream?.markdown(l10n.t('Conversation already compacted.')); + break; + } const result = await this._sdkSession.compactHistory(); if (result.success) { this._stream?.markdown(l10n.t('Compacted conversation.')); diff --git a/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts b/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts index 010be09de1a06..8ad278f199053 100644 --- a/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts +++ b/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts @@ -120,6 +120,9 @@ class MockSdkSession { async compactHistory() { return { success: true }; } + public chatMessages: unknown[] = [{ role: 'user', content: 'hi' }, { role: 'assistant', content: 'hello' }]; + async getChatMessages() { return this.chatMessages; } + async abort() { } isAbortable(): boolean { return true; } @@ -723,6 +726,21 @@ describe('CopilotCLISession', () => { expect(sdkSession.currentMode).toBe('interactive'); expect(stream.output.join('\n')).toContain('Compacted conversation.'); }); + + it('reports already-compacted when no new messages since last compaction (issue #311422)', async () => { + const session = await createSession(); + const stream = new MockChatResponseStream(); + session.attachStream(stream); + // Simulate post-compaction state: only the single summary message remains. + sdkSession.chatMessages = [{ role: 'system', content: 'summary' }]; + let compactCalled = false; + sdkSession.compactHistory = async () => { compactCalled = true; return { success: true }; }; + + await session.handleRequest({ id: '', toolInvocationToken: undefined as never }, { command: 'compact', prompt: '' }, [], undefined, authInfo, CancellationToken.None); + + expect(compactCalled).toBe(false); + expect(stream.output.join('\n')).toContain('Conversation already compacted.'); + }); }); describe('steering (sending messages to a busy session)', () => { From 3e7be0ab3ba8e14cac39acebc47a7bb13991ebea Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 21 Apr 2026 16:15:22 -0700 Subject: [PATCH 2/3] See if we can avoid using unknown in test Co-authored-by: Copilot --- .../chatSessions/copilotcli/node/test/copilotcliSession.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts b/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts index 8ad278f199053..e66facc7e067c 100644 --- a/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts +++ b/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts @@ -120,7 +120,7 @@ class MockSdkSession { async compactHistory() { return { success: true }; } - public chatMessages: unknown[] = [{ role: 'user', content: 'hi' }, { role: 'assistant', content: 'hello' }]; + public chatMessages: Awaited> = [{ role: 'user', content: 'hi' }, { role: 'assistant', content: 'hello' }]; async getChatMessages() { return this.chatMessages; } async abort() { } From 8195d0d4a9daf6a40a589e5e4e7307b90643c07d Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 21 Apr 2026 16:36:10 -0700 Subject: [PATCH 3/3] better handle feedback Co-authored-by: Copilot --- .../copilotcli/node/copilotcliSession.ts | 7 ++++++- .../node/test/copilotcliSession.spec.ts | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts b/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts index 468f1b0e924f6..3570f95b7736b 100644 --- a/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts +++ b/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotcliSession.ts @@ -879,8 +879,13 @@ export class CopilotCLISession extends DisposableStore implements ICopilotCLISes await this._sdkSession.initializeAndValidateTools(); this._sdkSession.currentMode = 'interactive'; // Mirror the Copilot CLI SDK's own `messages.length < 2` guard to - // avoid its "Nothing to compact." throw after a prior compaction. + // avoid its "Nothing to compact." throw, while distinguishing + // empty sessions from already-compacted sessions in the UI. const messages = await this._sdkSession.getChatMessages(); + if (messages.length === 0) { + this._stream?.markdown(l10n.t('Nothing to compact.')); + break; + } if (messages.length < 2) { this._stream?.markdown(l10n.t('Conversation already compacted.')); break; diff --git a/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts b/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts index e66facc7e067c..f958458f5a720 100644 --- a/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts +++ b/extensions/copilot/src/extension/chatSessions/copilotcli/node/test/copilotcliSession.spec.ts @@ -741,6 +741,21 @@ describe('CopilotCLISession', () => { expect(compactCalled).toBe(false); expect(stream.output.join('\n')).toContain('Conversation already compacted.'); }); + + it('reports nothing-to-compact on an empty session', async () => { + const session = await createSession(); + const stream = new MockChatResponseStream(); + session.attachStream(stream); + // Simulate a brand-new session with no conversation yet. + sdkSession.chatMessages = []; + let compactCalled = false; + sdkSession.compactHistory = async () => { compactCalled = true; return { success: true }; }; + + await session.handleRequest({ id: '', toolInvocationToken: undefined as never }, { command: 'compact', prompt: '' }, [], undefined, authInfo, CancellationToken.None); + + expect(compactCalled).toBe(false); + expect(stream.output.join('\n')).toContain('Nothing to compact.'); + }); }); describe('steering (sending messages to a busy session)', () => {