mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-11 05:30:03 -05:00
agentHost: handle no-op Copilot compaction errors (#321875)
Handle no-op Copilot compaction errors (#321827) Treat Copilot CLI history compact errors containing "nothing to compact" as a successful no-op so /compact completes with the standard completion message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import type { CopilotSession, ExitPlanModeRequest, MessageOptions, PermissionReq
|
||||
import { DeferredPromise } from '../../../../base/common/async.js';
|
||||
import { encodeBase64, VSBuffer } from '../../../../base/common/buffer.js';
|
||||
import { Emitter } from '../../../../base/common/event.js';
|
||||
import { CancellationError } from '../../../../base/common/errors.js';
|
||||
import { CancellationError, getErrorMessage } from '../../../../base/common/errors.js';
|
||||
import { escapeMarkdownSyntaxTokens } from '../../../../base/common/htmlContent.js';
|
||||
import { Disposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js';
|
||||
import { Schemas } from '../../../../base/common/network.js';
|
||||
@@ -1000,6 +1000,11 @@ export class CopilotAgentSession extends Disposable {
|
||||
await this._wrapper.session.rpc.history.compact();
|
||||
this.emitInitialMarkdown(localize('copilotAgent.compactionCompleted', "Compaction completed"));
|
||||
} catch (err) {
|
||||
if (getErrorMessage(err).toLowerCase().includes('nothing to compact')) {
|
||||
this.emitInitialMarkdown(localize('copilotAgent.compactionCompleted', "Compaction completed"));
|
||||
this._completeActiveTurn();
|
||||
return;
|
||||
}
|
||||
this._logService.error(err, `[Copilot:${this.sessionId}] rpc.history.compact failed`);
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ class MockCopilotSession {
|
||||
readonly commandListCalls: unknown[] = [];
|
||||
readonly commandInvokeCalls: Array<{ name: string; input?: string }> = [];
|
||||
compactResult: { success: boolean; tokensRemoved: number; messagesRemoved: number } = { success: true, tokensRemoved: 0, messagesRemoved: 0 };
|
||||
compactError: unknown = undefined;
|
||||
commandListResult: { commands: Array<{ name: string; kind: 'builtin' | 'skill' | 'client'; description: string; allowDuringAgentExecution: boolean }> } = { commands: [] };
|
||||
commandInvokeResult: { kind: 'text'; text: string; markdown?: boolean } | { kind: 'completed'; message?: string } | { kind: 'agent-prompt'; prompt: string; displayPrompt: string; mode?: 'interactive' | 'plan' | 'autopilot' } = { kind: 'text', text: '' };
|
||||
messages: SessionEvent[] = [];
|
||||
@@ -103,6 +104,9 @@ class MockCopilotSession {
|
||||
history: {
|
||||
compact: async (params?: unknown) => {
|
||||
this.compactCalls.push(params ?? null);
|
||||
if (this.compactError !== undefined) {
|
||||
throw this.compactError;
|
||||
}
|
||||
return this.compactResult;
|
||||
},
|
||||
},
|
||||
@@ -633,6 +637,36 @@ suite('CopilotAgentSession', () => {
|
||||
assert.ok(turnComplete, 'expected the turn to complete on a failed compaction');
|
||||
});
|
||||
|
||||
test('`/compact` treats nothing-to-compact errors as completed', async () => {
|
||||
const logService = new CapturingLogService();
|
||||
const { session, mockSession, signals } = await createAgentSession(disposables, { logService });
|
||||
mockSession.compactError = new Error('NOTHING TO COMPACT for this conversation');
|
||||
|
||||
await session.send('/compact', undefined, 'turn-compact');
|
||||
|
||||
const actions = getActions(signals);
|
||||
assert.deepStrictEqual({
|
||||
compactCalls: mockSession.compactCalls.length,
|
||||
sendRequests: mockSession.sendRequests,
|
||||
errors: logService.errors,
|
||||
responseParts: actions
|
||||
.filter(a => a.type === ActionType.ChatResponsePart)
|
||||
.map(a => {
|
||||
const part = (a as ChatResponsePartAction).part;
|
||||
return part.kind === ResponsePartKind.Markdown ? { turnId: a.turnId, kind: part.kind, content: part.content } : { turnId: a.turnId, kind: part.kind };
|
||||
}),
|
||||
turnComplete: actions
|
||||
.filter(a => a.type === ActionType.ChatTurnComplete)
|
||||
.map(a => (a as ChatTurnCompleteAction).turnId),
|
||||
}, {
|
||||
compactCalls: 1,
|
||||
sendRequests: [],
|
||||
errors: [],
|
||||
responseParts: [{ turnId: 'turn-compact', kind: ResponsePartKind.Markdown, content: 'Compaction completed' }],
|
||||
turnComplete: ['turn-compact'],
|
||||
});
|
||||
});
|
||||
|
||||
test('`/env` runs the runtime command when listed and emits markdown output', async () => {
|
||||
const { session, mockSession, signals } = await createAgentSession(disposables);
|
||||
mockSession.commandListResult = {
|
||||
|
||||
Reference in New Issue
Block a user