Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support checkpoints #1124

Draft
wants to merge 21 commits into
base: cs-main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
55c8133
Move aide code editing session
ghostwriternr Jan 16, 2025
81c15b2
Merge remote-tracking branch 'origin/cs-main' into support-checkpoints
ghostwriternr Feb 2, 2025
8965230
WIP: Start refactoring code editing service
ghostwriternr Feb 2, 2025
ade4d0e
Initialise new editing service from agent widget
ghostwriternr Feb 3, 2025
fd9e0ed
Separate checkpoints for requests & responses
ghostwriternr Feb 3, 2025
52976cf
Use exchangeId for telemetry event
ghostwriternr Feb 3, 2025
818b380
Migrate from codeEdit API to textEdit
ghostwriternr Feb 3, 2025
46bc68d
Initialize editor overlay widget
ghostwriternr Feb 4, 2025
bbc1b34
Migrate commands off edit preview widget
ghostwriternr Feb 4, 2025
d23f87e
Improve rendering of attachments
ghostwriternr Feb 5, 2025
814bee7
Make agentic responses look like 1 exchange
ghostwriternr Feb 5, 2025
1224890
Complete basic case for revert action
ghostwriternr Feb 5, 2025
7ac62ee
Show revert at requests & responses w/side-effects
ghostwriternr Feb 5, 2025
2ee9822
Cleanup codeblock rendering before move to pills
ghostwriternr Feb 6, 2025
4071486
Rudimentary support for showing diffs as pills
ghostwriternr Feb 6, 2025
8536f82
Revert "Rudimentary support for showing diffs as pills"
ghostwriternr Feb 6, 2025
0b61c2e
Disable reverts when request in progress
ghostwriternr Feb 6, 2025
8496dc5
Delete exchanges only on new request
ghostwriternr Feb 6, 2025
283fa2d
Improve hidden exchanges container styling
ghostwriternr Feb 6, 2025
829ed96
Remove empty exchanges and fix revertibility check
ghostwriternr Feb 7, 2025
5d15891
Handle redos
ghostwriternr Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 46 additions & 69 deletions extensions/codestory/src/chatState/convertStreamToMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,46 +702,40 @@ class DocumentManager {
// Replace a specific line and report the change
async replaceLine(index: number, newLine: AdjustedLineContent) {
this.lines[index] = new LineContent(newLine.adjustedContent, this.indentStyle);
//console.log('sidecar.replaceLine', index);
// console.table({
// 'edit': 'replace_line',
// 'index': index,
// 'content': newLine.adjustedContent,
// });
const edits = new vscode.WorkspaceEdit();
if (newLine.adjustedContent === '') {
// console.log('What line are we replaceLine', newLine.adjustedContent);
edits.delete(this.uri, new vscode.Range(index, 0, index, 1000), {
label: this.uniqueId.toString(),
needsConfirmation: false,
});
const edit = vscode.TextEdit.delete(
new vscode.Range(index, 0, index, 1000),
);
this.iterationEdits.delete(this.uri, new vscode.Range(index, 0, index, 1000));
if (this.applyDirectly) {
await vscode.workspace.applyEdit(edits);
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.set(this.uri, [edit]);
await vscode.workspace.applyEdit(workspaceEdit);
}
else if (this.limiter === null) {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
} else {
await this.limiter.queue(async () => {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
});
}
return index + 1;
} else {
// console.log('What line are we replaceLine', newLine.adjustedContent);
edits.replace(this.uri, new vscode.Range(index, 0, index, 1000), newLine.adjustedContent, {
label: this.uniqueId.toString(),
needsConfirmation: false,
});
const edit = vscode.TextEdit.replace(
new vscode.Range(index, 0, index, 1000),
newLine.adjustedContent
);
this.iterationEdits.replace(this.uri, new vscode.Range(index, 0, index, 1000), newLine.adjustedContent);
if (this.applyDirectly) {
await vscode.workspace.applyEdit(edits);
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.set(this.uri, [edit]);
await vscode.workspace.applyEdit(workspaceEdit);
}
else if (this.limiter === null) {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
} else {
await this.limiter.queue(async () => {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
});
}
return index + 1;
Expand All @@ -750,13 +744,6 @@ class DocumentManager {

// Replace multiple lines starting from a specific index
async replaceLines(startIndex: number, endIndex: number, newLine: AdjustedLineContent) {
//console.log('sidecar.replaceLine', startIndex, endIndex);
// console.table({
// 'edit': 'replace_lines',
// 'start_index': startIndex,
// 'end_index': endIndex,
// 'content': newLine.adjustedContent,
// });
if (startIndex === endIndex) {
return await this.replaceLine(startIndex, newLine);
} else {
Expand All @@ -765,21 +752,21 @@ class DocumentManager {
endIndex - startIndex + 1,
new LineContent(newLine.adjustedContent, this.indentStyle)
);
const edits = new vscode.WorkspaceEdit();
// console.log('sidecar.What line are we replaceLines', newLine.adjustedContent, startIndex, endIndex);
edits.replace(this.uri, new vscode.Range(startIndex, 0, endIndex, 1000), newLine.adjustedContent, {
label: this.uniqueId.toString(),
needsConfirmation: false,
});
const edit = vscode.TextEdit.replace(
new vscode.Range(startIndex, 0, endIndex, 1000),
newLine.adjustedContent
);
this.iterationEdits.replace(this.uri, new vscode.Range(startIndex, 0, endIndex, 1000), newLine.adjustedContent);
if (this.applyDirectly) {
await vscode.workspace.applyEdit(edits);
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.set(this.uri, [edit]);
await vscode.workspace.applyEdit(workspaceEdit);
}
else if (this.limiter === null) {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
} else {
await this.limiter.queue(async () => {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
});
}
return startIndex + 1;
Expand All @@ -788,57 +775,47 @@ class DocumentManager {

// Add a new line at the end
async appendLine(newLine: AdjustedLineContent) {
//console.log('sidecar.appendLine', this.lines.length - 1);
this.lines.push(new LineContent(newLine.adjustedContent, this.indentStyle));
const edits = new vscode.WorkspaceEdit();
// console.table({
// 'edit': 'append_line',
// 'start_index': this.lines.length - 2,
// 'content': newLine.adjustedContent,
// });
// console.log('what line are we appendLine', newLine.adjustedContent);
edits.replace(this.uri, new vscode.Range(this.lines.length - 2, 1000, this.lines.length - 2, 1000), '\n' + newLine.adjustedContent, {
label: this.uniqueId.toString(),
needsConfirmation: false,
});

const edit = vscode.TextEdit.replace(
new vscode.Range(this.lines.length - 2, 1000, this.lines.length - 2, 1000),
'\n' + newLine.adjustedContent
);
this.iterationEdits.replace(this.uri, new vscode.Range(this.lines.length - 2, 1000, this.lines.length - 2, 1000), '\n' + newLine.adjustedContent);
if (this.applyDirectly) {
await vscode.workspace.applyEdit(edits);
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.set(this.uri, [edit]);
await vscode.workspace.applyEdit(workspaceEdit);
}
else if (this.limiter === null) {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
} else {
await this.limiter.queue(async () => {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
});
}
return this.lines.length;
}

// Insert a new line after a specific index
async insertLineAfter(index: number, newLine: AdjustedLineContent) {
//console.log('insertLineAfter', index);
// console.table({
// 'edit': 'insert_line_after',
// 'index': index,
// 'content': newLine.adjustedContent,
// });
this.lines.splice(index + 1, 0, new LineContent(newLine.adjustedContent, this.indentStyle));
const edits = new vscode.WorkspaceEdit();
// console.log('what line are we inserting insertLineAfter', newLine.adjustedContent);
edits.replace(this.uri, new vscode.Range(index, 1000, index, 1000), '\n' + newLine.adjustedContent, {
label: this.uniqueId.toString(),
needsConfirmation: false,
});

const edit = vscode.TextEdit.replace(
new vscode.Range(index, 1000, index, 1000),
'\n' + newLine.adjustedContent
);
this.iterationEdits.replace(this.uri, new vscode.Range(index, 1000, index, 1000), '\n' + newLine.adjustedContent);
if (this.applyDirectly) {
await vscode.workspace.applyEdit(edits);
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.set(this.uri, [edit]);
await vscode.workspace.applyEdit(workspaceEdit);
}
else if (this.limiter === null) {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
} else {
await this.limiter.queue(async () => {
await this.progress.codeEdit(edits);
this.progress.textEdit(this.uri, edit);
});
}
return index + 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import * as http from 'http';
import * as net from 'net';
import * as os from 'os';
import * as vscode from 'vscode';

import { AnswerSplitOnNewLineAccumulatorStreaming, StreamProcessor } from '../../chatState/convertStreamToMessage';
import { CSEventHandler } from '../../csEvents/csEventHandler';
import postHogClient from '../../posthog/client';
import { applyEdits, applyEditsDirectly, } from '../../server/applyEdits';
import { createFileIfNotExists } from '../../server/createFile';
import { RecentEditsRetriever } from '../../server/editedFiles';
import { handleRequest } from '../../server/requestHandler';
import { EditedCodeStreamingRequest, SideCarAgentEvent, SidecarApplyEditsRequest, SidecarContextEvent, SidecarUndoPlanStep, ToolInputPartial } from '../../server/types';
import { EditedCodeStreamingRequest, SideCarAgentEvent, SidecarApplyEditsRequest, SidecarContextEvent, ToolInputPartial } from '../../server/types';
import { RepoRef, SideCarClient } from '../../sidecar/client';
import { getUniqueId, getUserId } from '../../utilities/uniqueId';
import { ProjectContext } from '../../utilities/workspaceContext';
Expand Down Expand Up @@ -157,7 +156,6 @@ export class AideAgentSessionProvider implements vscode.AideSessionParticipant {
this.provideEditStreamed.bind(this),
this.newExchangeIdForSession.bind(this),
recentEditsRetriever.retrieveSidecar.bind(recentEditsRetriever),
this.undoToCheckpoint.bind(this),
)
);
this.recentEditsRetriever = recentEditsRetriever;
Expand Down Expand Up @@ -200,39 +198,6 @@ export class AideAgentSessionProvider implements vscode.AideSessionParticipant {
await this.sidecarClient.sendContextRecording(events, this.editorUrl);
}

async undoToCheckpoint(request: SidecarUndoPlanStep): Promise<{
success: boolean;
}> {
const exchangeId = request.exchange_id;
const sessionId = request.session_id;
const planStep = request.index;
const responseStream = this.responseStreamCollection.getResponseStream({
sessionId,
exchangeId,
});
if (responseStream === undefined) {
return {
success: false,
};
}
let label = exchangeId;
if (planStep !== null) {
label = `${exchangeId}::${planStep}`;
}

// This creates a very special code edit which is handled by the aideAgentCodeEditingService
// where we intercept this edit and instead do a global rollback
const edit = new vscode.WorkspaceEdit();
edit.delete(vscode.Uri.file('/undoCheck'), new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)), {
label,
needsConfirmation: false,
});
responseStream.stream.codeEdit(edit);
return {
success: true,
};
}

async newExchangeIdForSession(sessionId: string): Promise<{
exchange_id: string | undefined;
}> {
Expand Down
8 changes: 4 additions & 4 deletions extensions/codestory/src/server/applyEdits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,19 @@ export async function applyEdits(
const range = new vscode.Range(new vscode.Position(startPosition.line, 0), new vscode.Position(endPosition.line, endPosition.character));
const fileUri = vscode.Uri.file(filePath);

const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.replace(fileUri, range, replacedText);
const edit = vscode.TextEdit.replace(range, replacedText);
iterationEdits.replace(fileUri, range, replacedText);
if (request.apply_directly) {
// apply the edits to it
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.set(fileUri, [edit]);
await vscode.workspace.applyEdit(workspaceEdit);
// we also want to save the file at this point after applying the edit
await vscode.workspace.save(fileUri);
} else {
await response.codeEdit(workspaceEdit);
response.textEdit(fileUri, edit);
}


// we calculate how many lines we get after replacing the text
// once we make the edit on the range, the new range is presented to us
// we have to calculate the new range and use that instead
Expand Down
9 changes: 1 addition & 8 deletions extensions/codestory/src/server/requestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as http from 'http';
import { SidecarApplyEditsRequest, LSPDiagnostics, SidecarGoToDefinitionRequest, SidecarGoToImplementationRequest, SidecarGoToReferencesRequest, SidecarOpenFileToolRequest, LSPQuickFixInvocationRequest, SidecarQuickFixRequest, SidecarSymbolSearchRequest, SidecarInlayHintsRequest, SidecarGetOutlineNodesRequest, SidecarOutlineNodesWithContentRequest, EditedCodeStreamingRequest, SidecarRecentEditsRetrieverRequest, SidecarRecentEditsRetrieverResponse, SidecarCreateFileRequest, LSPFileDiagnostics, SidecarGetPreviousWordRangeRequest, SidecarDiagnosticsResponse, SidecarCreateNewExchangeRequest, SidecarUndoPlanStep, SidecarExecuteTerminalCommandRequest, SidecarListFilesEndpoint } from './types';
import { SidecarApplyEditsRequest, LSPDiagnostics, SidecarGoToDefinitionRequest, SidecarGoToImplementationRequest, SidecarGoToReferencesRequest, SidecarOpenFileToolRequest, LSPQuickFixInvocationRequest, SidecarQuickFixRequest, SidecarSymbolSearchRequest, SidecarInlayHintsRequest, SidecarGetOutlineNodesRequest, SidecarOutlineNodesWithContentRequest, EditedCodeStreamingRequest, SidecarRecentEditsRetrieverRequest, SidecarRecentEditsRetrieverResponse, SidecarCreateFileRequest, LSPFileDiagnostics, SidecarGetPreviousWordRangeRequest, SidecarDiagnosticsResponse, SidecarCreateNewExchangeRequest, SidecarExecuteTerminalCommandRequest, SidecarListFilesEndpoint } from './types';
import { Position, Range, workspace } from 'vscode';
import { getDiagnosticsFromEditor, getEnrichedDiagnostics, getFileDiagnosticsFromEditor, getFullWorkspaceDiagnostics, getHoverInformation } from './diagnostics';
import { openFileEditor } from './openFile';
Expand Down Expand Up @@ -52,7 +52,6 @@ export function handleRequest(
exchange_id: string | undefined;
}>,
recentEditsRetriever: (request: SidecarRecentEditsRetrieverRequest) => Promise<SidecarRecentEditsRetrieverResponse>,
undoToCheckpoint: (request: SidecarUndoPlanStep) => Promise<{ success: boolean }>,
) {
return async (req: http.IncomingMessage, res: http.ServerResponse) => {
try {
Expand Down Expand Up @@ -207,12 +206,6 @@ export function handleRequest(
const response = await newExchangeId(request.session_id);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(response));
} else if (req.method === 'POST' && req.url === '/undo_session_changes') {
const body = await readRequestBody(req);
const request: SidecarUndoPlanStep = JSON.parse(body);
const response = await undoToCheckpoint(request);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(response));
} else if (req.method === 'POST' && req.url === '/rip_grep_path') {
const ripGrepPath = await getRipGrepPath();
res.writeHead(200, { 'Content-Type': 'application/json' });
Expand Down
7 changes: 6 additions & 1 deletion src/vs/platform/actions/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,12 @@ export class MenuId {
static readonly AideAgentInlineSymbolAnchorContext = new MenuId('AideAgentInlineSymbolAnchorContext');
// TODO(@ghostwriternr): This one isn't currently actually initialised anywhere
static readonly AideAgentInputSymbolAttachmentContext = new MenuId('AideAgentInputSymbolAttachmentContext');
static readonly AideAgentEditPreviewWidget = new MenuId('AideAgentEditPreviewWidget');
static readonly AideAgentEditingWidgetToolbar = new MenuId('AideAgentEditingWidgetToolbar');
static readonly AideAgentEditingEditorContent = new MenuId('AideAgentEditingEditorContent');
static readonly AideAgentEditingEditorHunk = new MenuId('AideAgentEditingEditorHunk');
static readonly AideAgentEditingWidgetModifiedFilesToolbar = new MenuId('AideAgentEditingWidgetModifiedFilesToolbar');
static readonly AideAgentEditingCodeBlockContext = new MenuId('AideAgentEditingCodeBlockContext');
static readonly AideAgentEditingRevertToolbar = new MenuId('AideAgentEditingRevertToolbar');
static readonly AccessibleView = new MenuId('AccessibleView');
static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar');
static readonly DiffEditorHunkToolbar = new MenuId('DiffEditorHunkToolbar');
Expand Down
1 change: 0 additions & 1 deletion src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1958,7 +1958,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
AideAgentScope: extHostTypes.AideAgentScope,
AideAgentReferenceKind: extHostTypes.AideAgentReferenceKind,
AideAgentToolTypeError: extHostTypes.AideAgentToolTypeError,
ChatResponseCodeEditPart: extHostTypes.ChatResponseCodeEditPart,
NewSymbolName: extHostTypes.NewSymbolName,
NewSymbolNameTag: extHostTypes.NewSymbolNameTag,
NewSymbolNameTriggerKind: extHostTypes.NewSymbolNameTriggerKind,
Expand Down
5 changes: 1 addition & 4 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import { SaveReason } from '../../common/editor.js';
import { IRevealOptions, ITreeItem, IViewBadge } from '../../common/views.js';
import { ChatAgentLocation as AideAgentLocation } from '../../contrib/aideAgent/common/aideAgentAgents.js';
import { IChatProgressResponseContent as IAideAgentProgressResponseContent } from '../../contrib/aideAgent/common/aideAgentModel.js';
import { IAideAgentPlanStep, IAideAgentProgressStage, IChatCodeEdit, IChatEndResponse, IAideAgentToolTypeError } from '../../contrib/aideAgent/common/aideAgentService.js';
import { IAideAgentPlanStep, IAideAgentProgressStage, IChatEndResponse, IAideAgentToolTypeError } from '../../contrib/aideAgent/common/aideAgentService.js';
import { DevtoolsStatus, InspectionResult } from '../../contrib/aideAgent/common/devtoolsService.js';
import { SidecarDownloadStatus, SidecarRunningStatus } from '../../contrib/aideAgent/common/sidecarService.js';
import { CallHierarchyItem } from '../../contrib/callHierarchy/common/callHierarchy.js';
Expand Down Expand Up @@ -1461,11 +1461,8 @@ export interface ExtHostAideAgentAgentsShape {
$releaseSession(sessionId: string): void;
}

export type IChatCodeEditDto = Pick<IChatCodeEdit, 'kind'> & { edits: IWorkspaceEditDto };

export type IAideAgentProgressDto =
| IChatProgressDto
| IChatCodeEditDto
| Dto<IAideAgentPlanStep | IAideAgentProgressStage | IAideAgentToolTypeError | IChatEndResponse>;

export type IAideAgentContentProgressDto =
Expand Down
9 changes: 0 additions & 9 deletions src/vs/workbench/api/common/extHostAideAgentAgents2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,6 @@ class AideAgentResponseStream {
_report(dto);
return this;
},
codeEdit(edits) {
throwIfDone(this.codeEdit);

const part = new extHostTypes.ChatResponseCodeEditPart(edits);
const dto = typeConvert.ChatResponseCodeEditPart.from(part);
_report(dto);
return this;
},
detectedParticipant(participant, command) {
throwIfDone(this.detectedParticipant);

Expand All @@ -240,7 +232,6 @@ class AideAgentResponseStream {

if (
part instanceof extHostTypes.ChatResponseTextEditPart ||
part instanceof extHostTypes.ChatResponseCodeEditPart ||
part instanceof extHostTypes.ChatResponseMarkdownWithVulnerabilitiesPart ||
part instanceof extHostTypes.ChatResponseDetectedParticipantPart ||
part instanceof extHostTypes.ChatResponseWarningPart ||
Expand Down
Loading