Skip to content

Commit fccc441

Browse files
authored
Improve chat session log tool call rendering (read/edit files range, bash tools). (#7720)
* Improve chat session log tool call rendering (read/edit files range, bash tools). * fix typings
1 parent b5a63e5 commit fccc441

4 files changed

Lines changed: 71 additions & 20 deletions

File tree

common/sessionParsing.ts

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export interface StrReplaceEditorToolData {
6262
filePath?: string;
6363
fileLabel?: string;
6464
parsedContent?: { content: string; fileA: string | undefined; fileB: string | undefined; };
65+
viewRange?: { start: number, end: number }
6566
}
6667

6768
export namespace StrReplaceEditorToolData {
@@ -110,6 +111,32 @@ export function parseDiff(content: string): { content: string; fileA: string | u
110111
};
111112
}
112113

114+
export function parseRange(view_range: unknown): { start: number, end: number } | undefined {
115+
if (!view_range) {
116+
return undefined;
117+
}
118+
119+
if (!Array.isArray(view_range)) {
120+
return undefined;
121+
}
122+
123+
if (view_range.length !== 2) {
124+
return undefined;
125+
}
126+
127+
const start = view_range[0];
128+
const end = view_range[1];
129+
130+
if (typeof start !== 'number' || typeof end !== 'number') {
131+
return undefined;
132+
}
133+
134+
return {
135+
start,
136+
end
137+
};
138+
}
139+
113140

114141

115142
/**
@@ -133,7 +160,7 @@ export function parseToolCallDetails(
133160
},
134161
content: string
135162
): ParsedToolCallDetails {
136-
let args: { command?: string, path?: string, prDescription?: string, commitMessage?: string } = {};
163+
let args: { command?: string, path?: string, prDescription?: string, commitMessage?: string, view_range?: unknown } = {};
137164
try {
138165
args = toolCall.function.arguments ? JSON.parse(toolCall.function.arguments) : {};
139166
} catch {
@@ -145,18 +172,20 @@ export function parseToolCallDetails(
145172
if (name === 'str_replace_editor') {
146173
if (args.command === 'view') {
147174
const parsedContent = parseDiff(content);
175+
const parsedRange = parseRange(args.view_range);
148176
if (parsedContent) {
149177
const file = parsedContent.fileA ?? parsedContent.fileB;
150178
const fileLabel = file && toFileLabel(file);
151179
return {
152180
toolName: fileLabel === '' ? 'Read repository' : 'Read',
153-
invocationMessage: fileLabel ? `Read [](${fileLabel})` : 'Read repository',
154-
pastTenseMessage: fileLabel ? `Read [](${fileLabel})` : 'Read repository',
181+
invocationMessage: fileLabel ? (`Read [](${fileLabel})` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Read repository',
182+
pastTenseMessage: fileLabel ? (`Read [](${fileLabel})` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Read repository',
155183
toolSpecificData: fileLabel ? {
156184
command: 'view',
157185
filePath: file,
158186
fileLabel: fileLabel,
159-
parsedContent: parsedContent
187+
parsedContent: parsedContent,
188+
viewRange: parsedRange
160189
} : undefined
161190
};
162191
} else {
@@ -167,9 +196,9 @@ export function parseToolCallDetails(
167196
fileLabel = filePath;
168197

169198
return {
170-
toolName: fileLabel ? `Read ${fileLabel}` : 'Read repository',
171-
invocationMessage: fileLabel ? `Read ${fileLabel}` : 'Read repository',
172-
pastTenseMessage: fileLabel ? `Read ${fileLabel}` : 'Read repository',
199+
toolName: fileLabel ? (`Read ${fileLabel}` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Read repository',
200+
invocationMessage: fileLabel ? (`Read ${fileLabel}` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Read repository',
201+
pastTenseMessage: fileLabel ? (`Read ${fileLabel}` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Read repository',
173202
};
174203
} else if (fileLabel === '') {
175204
return {
@@ -180,41 +209,48 @@ export function parseToolCallDetails(
180209
} else {
181210
return {
182211
toolName: `Read`,
183-
invocationMessage: `Read ${fileLabel}`,
184-
pastTenseMessage: `Read ${fileLabel}`,
212+
invocationMessage: (`Read ${fileLabel}` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')),
213+
pastTenseMessage: (`Read ${fileLabel}` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')),
185214
toolSpecificData: {
186215
command: 'view',
187216
filePath: filePath,
188-
fileLabel: fileLabel
217+
fileLabel: fileLabel,
218+
viewRange: parsedRange
189219
}
190220
};
191221
}
192222
}
193223
} else {
194224
const filePath = args.path;
195225
const fileLabel = filePath && toFileLabel(filePath);
226+
const parsedRange = parseRange(args.view_range);
227+
196228
return {
197229
toolName: 'Edit',
198-
invocationMessage: fileLabel ? `Edit [](${fileLabel})` : 'Edit',
199-
pastTenseMessage: fileLabel ? `Edit [](${fileLabel})` : 'Edit',
230+
invocationMessage: fileLabel ? (`Edit [](${fileLabel})` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Edit',
231+
pastTenseMessage: fileLabel ? (`Edit [](${fileLabel})` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Edit',
200232
toolSpecificData: fileLabel ? {
201233
command: args.command || 'edit',
202234
filePath: filePath,
203-
fileLabel: fileLabel
235+
fileLabel: fileLabel,
236+
viewRange: parsedRange
204237
} : undefined
205238
};
206239
}
207240
} else if (name === 'view') {
208241
const filePath = args.path;
209242
const fileLabel = filePath && toFileLabel(filePath);
243+
const parsedRange = parseRange(args.view_range);
244+
210245
return {
211246
toolName: fileLabel === '' ? 'Read repository' : 'Read',
212-
invocationMessage: fileLabel ? `Read [](${fileLabel})` : 'Read repository',
213-
pastTenseMessage: fileLabel ? `Read [](${fileLabel})` : 'Read repository',
247+
invocationMessage: fileLabel ? (`Read [](${fileLabel})` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Read repository',
248+
pastTenseMessage: fileLabel ? (`Read [](${fileLabel})` + (parsedRange ? `, lines ${parsedRange.start} to ${parsedRange.end}` : '')) : 'Read repository',
214249
toolSpecificData: fileLabel ? {
215250
command: 'view',
216251
filePath: filePath,
217-
fileLabel: fileLabel
252+
fileLabel: fileLabel,
253+
viewRange: parsedRange
218254
} : undefined
219255
};
220256
} else if (name === 'think') {
@@ -250,6 +286,16 @@ export function parseToolCallDetails(
250286
details.toolSpecificData = bashToolData;
251287
}
252288
return details;
289+
} else if (name === 'read_bash') {
290+
return {
291+
toolName: 'read_bash',
292+
invocationMessage: 'Read logs from Bash session'
293+
}
294+
} else if (name === 'stop_bash') {
295+
return {
296+
toolName: 'stop_bash',
297+
invocationMessage: 'Stop Bash session'
298+
}
253299
} else {
254300
// Unknown tool type
255301
return {

src/@types/vscode.proposed.chatSessionsProvider.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ declare module 'vscode' {
4141
* @returns Metadata for the chat session
4242
*/
4343
provideNewChatSessionItem?(options: {
44+
/**
45+
* The chat request that initiated the session creation
46+
*/
47+
readonly request: ChatRequest;
48+
4449
/**
4550
* Initial prompt to initiate the session
4651
*/
@@ -210,4 +215,4 @@ declare module 'vscode' {
210215
*/
211216
export function showChatSession(chatSessionType: string, sessionId: string, options: ChatSessionShowOptions): Thenable<void>;
212217
}
213-
}
218+
}

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ async function deferredActivate(context: vscode.ExtensionContext, showPRControll
433433
return await copilotRemoteAgentManager.provideChatSessionContent(id, token);
434434
};
435435
onDidChangeChatSessionItems = copilotRemoteAgentManager.onDidChangeChatSessions;
436-
provideNewChatSessionItem = async (options: { prompt?: string; history: ReadonlyArray<vscode.ChatRequestTurn | vscode.ChatResponseTurn>; metadata?: any; }, token: vscode.CancellationToken): Promise<vscode.ChatSessionItem> => {
436+
provideNewChatSessionItem = async (options: { readonly request: vscode.ChatRequest; prompt?: string; history: ReadonlyArray<vscode.ChatRequestTurn | vscode.ChatResponseTurn>; metadata?: any; }, token: vscode.CancellationToken): Promise<vscode.ChatSessionItem> => {
437437
return await copilotRemoteAgentManager.provideNewChatSessionItem(options, token);
438438
};
439439
}();

src/github/copilotRemoteAgent/chatSessionContentBuilder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,9 @@ export class ChatSessionContentBuilder {
376376
if (StrReplaceEditorToolData.is(toolDetails.toolSpecificData)) {
377377
if ((toolDetails.toolSpecificData.command === 'view' || toolDetails.toolSpecificData.command === 'edit') && toolDetails.toolSpecificData.fileLabel) {
378378
const uri = vscode.Uri.file(nodePath.join(pullRequest.githubRepository.rootUri.fsPath, toolDetails.toolSpecificData.fileLabel));
379-
toolPart.invocationMessage = new vscode.MarkdownString(`${toolPart.toolName} [](${uri.toString()})`);
379+
toolPart.invocationMessage = new vscode.MarkdownString(`${toolPart.toolName} [](${uri.toString()})` + (toolDetails.toolSpecificData?.viewRange ? `, lines ${toolDetails.toolSpecificData.viewRange?.start} to ${toolDetails.toolSpecificData.viewRange?.end}` : ''));
380380
toolPart.invocationMessage.supportHtml = true;
381-
toolPart.pastTenseMessage = new vscode.MarkdownString(`${toolPart.toolName} [](${uri.toString()})`);
381+
toolPart.pastTenseMessage = new vscode.MarkdownString(`${toolPart.toolName} [](${uri.toString()})` + (toolDetails.toolSpecificData?.viewRange ? `, lines ${toolDetails.toolSpecificData.viewRange?.start} to ${toolDetails.toolSpecificData.viewRange?.end}` : ''));
382382
}
383383
} else {
384384
toolPart.toolSpecificData = toolDetails.toolSpecificData;

0 commit comments

Comments
 (0)