Skip to content

Commit f074e7f

Browse files
committed
provide better data for tool calls
1 parent 4485827 commit f074e7f

File tree

3 files changed

+191
-29
lines changed

3 files changed

+191
-29
lines changed

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,35 @@ declare module 'vscode' {
8383
toolName: string;
8484
constructor(toolName: string);
8585
}
86+
export interface ChatTerminalToolInvocationData {
87+
command: string;
88+
language: string;
89+
}
90+
91+
export interface ChatTerminalToolInvocationData2 {
92+
commandLine: {
93+
original: string;
94+
userEdited?: string;
95+
toolEdited?: string;
96+
};
97+
language: string;
98+
}
99+
100+
export class ChatToolInvocationPart {
101+
toolName: string;
102+
toolCallId: string;
103+
isError?: boolean;
104+
invocationMessage?: string | MarkdownString;
105+
originMessage?: string | MarkdownString;
106+
pastTenseMessage?: string | MarkdownString;
107+
isConfirmed?: boolean;
108+
isComplete?: boolean;
109+
toolSpecificData?: ChatTerminalToolInvocationData | ChatTerminalToolInvocationData2;
110+
111+
constructor(toolName: string, toolCallId: string, isError?: boolean);
112+
}
86113

87-
export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseNotebookEditPart | ChatResponseConfirmationPart | ChatResponseCodeCitationPart | ChatResponseReferencePart2 | ChatResponseMovePart | ChatResponseExtensionsPart | ChatPrepareToolInvocationPart;
114+
export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseNotebookEditPart | ChatResponseConfirmationPart | ChatResponseCodeCitationPart | ChatResponseReferencePart2 | ChatResponseMovePart | ChatResponseExtensionsPart | ChatPrepareToolInvocationPart | ChatToolInvocationPart;
88115

89116
export class ChatResponseWarningPart {
90117
value: MarkdownString;

src/extension.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,6 @@ async function deferredActivate(context: vscode.ExtensionContext, showPRControll
411411

412412
const copilotRemoteAgentManager = new CopilotRemoteAgentManager(credentialStore, reposManager, telemetry);
413413
context.subscriptions.push(copilotRemoteAgentManager);
414-
<<<<<<< Updated upstream
415414
if (vscode.chat?.registerChatSessionItemProvider) {
416415
context.subscriptions.push(vscode.chat?.registerChatSessionItemProvider(
417416
'copilot-swe-agent',
@@ -420,27 +419,14 @@ async function deferredActivate(context: vscode.ExtensionContext, showPRControll
420419
provideChatSessionItems: async (token) => {
421420
return await copilotRemoteAgentManager.provideChatSessions(token);
422421
},
422+
provideChatSessionContent: async (id, token) => {
423+
return await copilotRemoteAgentManager.provideChatSessionContent(id, token);
424+
},
423425
// Events not used yet, but required by interface.
424426
onDidChangeChatSessionItems: new vscode.EventEmitter<void>().event,
425427
}
426428
));
427429
}
428-
=======
429-
context.subscriptions.push(vscode.chat.registerChatSessionItemProvider(
430-
'copilot-swe-agent',
431-
{
432-
label: vscode.l10n.t('GitHub Copilot Coding Agent'),
433-
provideChatSessionItems: async (token) => {
434-
return await copilotRemoteAgentManager.provideChatSessions(token);
435-
},
436-
provideChatSessionContent: async (id, token) => {
437-
return await copilotRemoteAgentManager.provideChatSessionContent(id, token);
438-
},
439-
// Events not used yet, but required by interface.
440-
onDidChangeChatSessionItems: new vscode.EventEmitter<void>().event,
441-
}
442-
));
443-
>>>>>>> Stashed changes
444430

445431
const prTree = new PullRequestsTreeDataProvider(telemetry, context, reposManager, copilotRemoteAgentManager);
446432
context.subscriptions.push(prTree);

src/github/copilotRemoteAgent.ts

Lines changed: 160 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ export class CopilotRemoteAgentManager extends Disposable {
665665

666666
// If session is in progress, try to fetch workflow steps to show setup progress
667667
let setupSteps: SessionSetupStep[] | undefined;
668-
if (session.state === 'in_progress' || logs.trim().length === 0 || true) {
668+
if (session.state === 'in_progress' || logs.trim().length === 0) {
669669
try {
670670
// Get workflow steps instead of logs
671671
setupSteps = await this.getWorkflowStepsFromAction(pullRequest);
@@ -828,19 +828,31 @@ export class CopilotRemoteAgentManager extends Disposable {
828828

829829
let currentRequestContent = '';
830830
let currentResponseContent = '';
831+
let currentResponseParts: Array<vscode.ChatResponseMarkdownPart | vscode.ChatToolInvocationPart> = [];
831832
let isCollectingUserMessage = false;
832833

833834
for (const chunk of logChunks) {
834835
for (const choice of chunk.choices) {
835836
const delta = choice.delta;
836837

838+
// Log finish_reason for debugging
839+
if (choice.finish_reason) {
840+
console.log(`Tool call finish_reason: ${choice.finish_reason}`);
841+
Logger.appendLine(`Tool call finish_reason: ${choice.finish_reason}`, CopilotRemoteAgentManager.ID);
842+
}
843+
837844
if (delta.role === 'user') {
838845
// If we were collecting a response, finalize it
839-
if (currentResponseContent.trim()) {
840-
const responseParts = [new vscode.ChatResponseMarkdownPart(currentResponseContent.trim())];
846+
if (currentResponseContent.trim() || currentResponseParts.length > 0) {
847+
// Add any remaining content as markdown
848+
if (currentResponseContent.trim()) {
849+
currentResponseParts.push(new vscode.ChatResponseMarkdownPart(currentResponseContent.trim()));
850+
}
851+
841852
const responseResult: vscode.ChatResult = {};
842-
history.push(new vscode.ChatResponseTurn2(responseParts, responseResult, 'copilot-swe-agent'));
853+
history.push(new vscode.ChatResponseTurn2(currentResponseParts, responseResult, 'copilot-swe-agent'));
843854
currentResponseContent = '';
855+
currentResponseParts = [];
844856
}
845857

846858
isCollectingUserMessage = true;
@@ -865,14 +877,81 @@ export class CopilotRemoteAgentManager extends Disposable {
865877
}
866878

867879
if (delta.content) {
868-
currentResponseContent += delta.content;
880+
if (delta.content.startsWith('<pr_title>')) {
881+
// the end
882+
} else {
883+
currentResponseContent += delta.content;
884+
}
869885
}
870886

871-
// Handle tool calls as code blocks in the response
887+
// Handle tool calls by creating parts immediately
872888
if (delta.tool_calls) {
889+
// If there's accumulated content, add it as markdown first
890+
if (currentResponseContent.trim()) {
891+
currentResponseParts.push(new vscode.ChatResponseMarkdownPart(currentResponseContent.trim()));
892+
currentResponseContent = '';
893+
}
894+
873895
for (const toolCall of delta.tool_calls) {
874-
if (toolCall.function?.name && toolCall.function?.arguments) {
875-
currentResponseContent += `\n\n**Tool Call: ${toolCall.function.name}**\n\`\`\`json\n${toolCall.function.arguments}\n\`\`\`\n`;
896+
console.log(`toolcall ${JSON.stringify(toolCall)}`);
897+
if (toolCall.function?.name && toolCall.id) {
898+
const toolPart = new vscode.ChatToolInvocationPart(toolCall.function.name, toolCall.id);
899+
toolPart.isComplete = true;
900+
toolPart.isError = false;
901+
toolPart.isConfirmed = true;
902+
903+
// Parse tool arguments and set tool-specific metadata following sessionView.tsx patterns
904+
const name = toolCall.function.name;
905+
const content = delta.content || '';
906+
907+
try {
908+
const args = toolCall.function.arguments ? JSON.parse(toolCall.function.arguments) : {};
909+
910+
if (name === 'str_replace_editor') {
911+
if (args.command === 'view') {
912+
toolPart.toolName = args.path ? `View ${this.toFileLabel(args.path)}` : 'View repository';
913+
toolPart.invocationMessage = `View ${args.path}`;
914+
toolPart.pastTenseMessage = `View ${args.path}`;
915+
} else {
916+
toolPart.toolName = 'Edit';
917+
toolPart.invocationMessage = `Edit: ${args.path}`;
918+
toolPart.pastTenseMessage = `Edit: ${args.path}`;
919+
}
920+
} else if (name === 'think') {
921+
toolPart.toolName = 'Thought';
922+
toolPart.invocationMessage = content;
923+
} else if (name === 'report_progress') {
924+
toolPart.toolName = 'Progress Update';
925+
toolPart.invocationMessage = args.prDescription || content;
926+
if (args.commitMessage) {
927+
toolPart.originMessage = `Commit: ${args.commitMessage}`;
928+
}
929+
} else if (name === 'bash') {
930+
toolPart.toolName = 'Run Bash command';
931+
const command = args.command ? `$ ${args.command}` : undefined;
932+
const bashContent = [command, content].filter(Boolean).join('\n');
933+
toolPart.invocationMessage = new vscode.MarkdownString(`\`\`\`bash\n${bashContent}\n\`\`\``);
934+
935+
// Use the terminal-specific data for bash commands
936+
if (args.command) {
937+
toolPart.toolSpecificData = {
938+
command: args.command,
939+
language: 'bash'
940+
};
941+
}
942+
} else {
943+
// Unknown tool type
944+
toolPart.toolName = name || 'unknown';
945+
toolPart.invocationMessage = new vscode.MarkdownString(`\`\`\`plaintext\n${content}\n\`\`\``);
946+
}
947+
} catch (error) {
948+
// Fallback for parsing errors
949+
toolPart.toolName = name || 'unknown';
950+
toolPart.invocationMessage = new vscode.MarkdownString(`\`\`\`plaintext\n${content}\n\`\`\``);
951+
toolPart.isError = true;
952+
}
953+
954+
currentResponseParts.push(toolPart);
876955
}
877956
}
878957
}
@@ -892,10 +971,14 @@ export class CopilotRemoteAgentManager extends Disposable {
892971
[] // toolReferences
893972
);
894973
history.push(userMessage);
895-
} else if (currentResponseContent.trim()) {
896-
const responseParts = [new vscode.ChatResponseMarkdownPart(currentResponseContent.trim())];
974+
} else if (currentResponseContent.trim() || currentResponseParts.length > 0) {
975+
// Add any remaining content as markdown
976+
if (currentResponseContent.trim()) {
977+
currentResponseParts.push(new vscode.ChatResponseMarkdownPart(currentResponseContent.trim()));
978+
}
979+
897980
const responseResult: vscode.ChatResult = {};
898-
history.push(new vscode.ChatResponseTurn2(responseParts, responseResult, 'copilot-swe-agent'));
981+
history.push(new vscode.ChatResponseTurn2(currentResponseParts, responseResult, 'copilot-swe-agent'));
899982
}
900983

901984
return history;
@@ -904,6 +987,72 @@ export class CopilotRemoteAgentManager extends Disposable {
904987
return [];
905988
}
906989
}
990+
991+
/**
992+
* Helper method to convert absolute file paths to relative labels
993+
* Following the pattern from sessionView.tsx
994+
*/
995+
private toFileLabel(file: string): string {
996+
// File paths are absolute and look like: `/home/runner/work/repo/repo/<path>`
997+
const parts = file.split('/');
998+
return parts.slice(6).join('/');
999+
}
1000+
1001+
/**
1002+
* Helper method to get language for a file based on its extension
1003+
* Following the pattern from sessionView.tsx
1004+
*/
1005+
private getLanguageForFile(filePath: string): string {
1006+
const extension = filePath.split('.').pop()?.toLowerCase();
1007+
1008+
// Common language mappings
1009+
const languageMap: { [ext: string]: string } = {
1010+
'ts': 'typescript',
1011+
'tsx': 'tsx',
1012+
'js': 'javascript',
1013+
'jsx': 'jsx',
1014+
'py': 'python',
1015+
'json': 'json',
1016+
'md': 'markdown',
1017+
'yml': 'yaml',
1018+
'yaml': 'yaml',
1019+
'xml': 'xml',
1020+
'html': 'html',
1021+
'css': 'css',
1022+
'scss': 'scss',
1023+
'less': 'less',
1024+
'sh': 'bash',
1025+
'bash': 'bash',
1026+
'zsh': 'bash',
1027+
'fish': 'bash',
1028+
'ps1': 'powershell',
1029+
'sql': 'sql',
1030+
'go': 'go',
1031+
'rs': 'rust',
1032+
'cpp': 'cpp',
1033+
'c': 'c',
1034+
'h': 'c',
1035+
'hpp': 'cpp',
1036+
'java': 'java',
1037+
'kt': 'kotlin',
1038+
'swift': 'swift',
1039+
'rb': 'ruby',
1040+
'php': 'php',
1041+
'cs': 'csharp',
1042+
'fs': 'fsharp',
1043+
'vb': 'vb',
1044+
'r': 'r',
1045+
'scala': 'scala',
1046+
'clj': 'clojure',
1047+
'elm': 'elm',
1048+
'dart': 'dart',
1049+
'lua': 'lua',
1050+
'perl': 'perl',
1051+
'vim': 'vim'
1052+
};
1053+
1054+
return extension ? languageMap[extension] || 'plaintext' : 'plaintext';
1055+
}
9071056
}
9081057

9091058
function parseSessionLogs(rawText: string): SessionResponseLogChunk[] {

0 commit comments

Comments
 (0)