@@ -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
9091058function parseSessionLogs ( rawText : string ) : SessionResponseLogChunk [ ] {
0 commit comments