Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita45336d

Browse files
committed
Pre-release 0.46.157
1 parent53360f8 commita45336d

File tree

12 files changed

+185
-80
lines changed

12 files changed

+185
-80
lines changed

‎Core/Sources/ChatService/Extensions/ChatService+FileEdit.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extension ChatService {
2525

2626
switch fileEdit.toolName{
2727
case.insertEditIntoFile:
28-
InsertEditIntoFileTool.applyEdit(for: fileURL, content: fileEdit.originalContent, contextProvider:self)
28+
InsertEditIntoFileTool.applyEdit(for: fileURL, content: fileEdit.originalContent)
2929
case.createFile:
3030
tryCreateFileTool.undo(for: fileURL)
3131
default:

‎Core/Sources/ChatService/ToolCalls/InsertEditIntoFileTool.swift‎

Lines changed: 91 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ import JSONRPC
77
import Logger
88
import XcodeInspector
99
import ChatAPIService
10+
import SystemUtils
11+
import Workspace
1012

1113
publicenumInsertEditError:LocalizedError{
1214
case missingEditorElement(file:URL)
1315
case openingApplicationUnavailable
1416
case fileNotOpenedInXcode
1517
case fileURLMismatch(expected:URL, actual:URL?)
18+
case fileNotAccessible(URL)
19+
case fileHasUnsavedChanges(URL)
1620

1721
publicvarerrorDescription:String?{
1822
switchself{
@@ -24,6 +28,10 @@ public enum InsertEditError: LocalizedError {
2428
return"The file is not currently opened in Xcode."
2529
case.fileURLMismatch(let expected,let actual):
2630
return"The currently focused file URL\(actual?.lastPathComponent??"unknown") does not match the expected file URL\(expected.lastPathComponent)."
31+
case.fileNotAccessible(let fileURL):
32+
return"The file\(fileURL.lastPathComponent) is not accessible."
33+
case.fileHasUnsavedChanges(let fileURL):
34+
return"The file\(fileURL.lastPathComponent) seems to have unsaved changes in Xcode. Please save the file and try again."
2735
}
2836
}
2937
}
@@ -50,7 +58,7 @@ public class InsertEditIntoFileTool: ICopilotTool {
5058
letfileURL=URL(fileURLWithPath: filePath)
5159
letoriginalContent=tryString(contentsOf: fileURL, encoding:.utf8)
5260

53-
InsertEditIntoFileTool.applyEdit(for: fileURL, content: code, contextProvider: contextProvider){ newContent, errorin
61+
InsertEditIntoFileTool.applyEdit(for: fileURL, content: code){ newContent, errorin
5462
iflet error= error{
5563
self.completeResponse(
5664
request,
@@ -106,7 +114,6 @@ public class InsertEditIntoFileTool: ICopilotTool {
106114
publicstaticfunc applyEdit(
107115
for fileURL:URL,
108116
content:String,
109-
contextProvider:anyToolContextProvider,
110117
xcodeInstance:AppInstanceInspector
111118
)throws->String{
112119
guardlet editorElement=Self.getEditorElement(by: xcodeInstance, for: fileURL)
@@ -155,39 +162,21 @@ public class InsertEditIntoFileTool: ICopilotTool {
155162
publicstaticfunc applyEdit(
156163
for fileURL:URL,
157164
content:String,
158-
contextProvider:anyToolContextProvider,
159165
completion:((String?,Error?)->Void)?=nil
160166
){
161-
NSWorkspace.openFileInXcode(fileURL: fileURL){ app, errorin
162-
do{
163-
iflet error= error{throw error}
164-
165-
guardlet app= app
166-
else{
167-
throwInsertEditError.openingApplicationUnavailable
168-
}
169-
170-
letappInstanceInspector=AppInstanceInspector(runningApplication: app)
171-
guard appInstanceInspector.isXcode
172-
else{
173-
throwInsertEditError.fileNotOpenedInXcode
174-
}
175-
176-
letnewContent=tryapplyEdit(
177-
for: fileURL,
178-
content: content,
179-
contextProvider: contextProvider,
180-
xcodeInstance: appInstanceInspector
181-
)
182-
183-
Task{
184-
awaitWorkspaceInvocationCoordinator().invokeFilespaceUpdate(fileURL: fileURL, content: newContent)
185-
iflet completion= completion{completion(newContent,nil)}
186-
}
187-
}catch{
188-
iflet completion= completion{completion(nil, error)}
189-
Logger.client.info("Failed to apply edit for file at\(fileURL),\(error)")
190-
}
167+
ifSystemUtils.isDeveloperMode ||SystemUtils.isPrereleaseBuild{
168+
/// Experimental solution: Use file system write for better reliability. Only enable in dev mode or prerelease builds.
169+
Self.applyEditWithFileSystem(
170+
for: fileURL,
171+
content: content,
172+
completion: completion
173+
)
174+
}else{
175+
Self.applyEditWithAccessibilityAPI(
176+
for: fileURL,
177+
content: content,
178+
completion: completion
179+
)
191180
}
192181
}
193182

@@ -248,3 +237,72 @@ private extension AppInstanceInspector {
248237
appElement.realtimeDocumentURL
249238
}
250239
}
240+
241+
extensionInsertEditIntoFileTool{
242+
staticfunc applyEditWithFileSystem(
243+
for fileURL:URL,
244+
content:String,
245+
completion:((String?,Error?)->Void)?=nil
246+
){
247+
do{
248+
guardlet diskFileContent=try?String(contentsOf: fileURL)else{
249+
throwInsertEditError.fileNotAccessible(fileURL)
250+
}
251+
252+
iflet focusedElement=XcodeInspector.shared.focusedElement,
253+
focusedElement.isSourceEditor,
254+
focusedElement.value!= diskFileContent
255+
{
256+
throwInsertEditError.fileHasUnsavedChanges(fileURL)
257+
}
258+
259+
// write content to disk
260+
try content.write(to: fileURL, atomically:true, encoding:.utf8)
261+
262+
Task{@WorkspaceActorin
263+
awaitWorkspaceInvocationCoordinator().invokeFilespaceUpdate(fileURL: fileURL, content: content)
264+
iflet completion= completion{completion(content,nil)}
265+
}
266+
}catch{
267+
iflet completion= completion{completion(nil, error)}
268+
Logger.client.info("Failed to apply edit for file at\(fileURL),\(error)")
269+
}
270+
}
271+
272+
staticfunc applyEditWithAccessibilityAPI(
273+
for fileURL:URL,
274+
content:String,
275+
completion:((String?,Error?)->Void)?=nil,
276+
){
277+
NSWorkspace.openFileInXcode(fileURL: fileURL){ app, errorin
278+
do{
279+
iflet error= error{throw error}
280+
281+
guardlet app= app
282+
else{
283+
throwInsertEditError.openingApplicationUnavailable
284+
}
285+
286+
letappInstanceInspector=AppInstanceInspector(runningApplication: app)
287+
guard appInstanceInspector.isXcode
288+
else{
289+
throwInsertEditError.fileNotOpenedInXcode
290+
}
291+
292+
letnewContent=tryapplyEdit(
293+
for: fileURL,
294+
content: content,
295+
xcodeInstance: appInstanceInspector
296+
)
297+
298+
Task{
299+
awaitWorkspaceInvocationCoordinator().invokeFilespaceUpdate(fileURL: fileURL, content: newContent)
300+
iflet completion= completion{completion(newContent,nil)}
301+
}
302+
}catch{
303+
iflet completion= completion{completion(nil, error)}
304+
Logger.client.info("Failed to apply edit for file at\(fileURL),\(error)")
305+
}
306+
}
307+
}
308+
}

‎Core/Sources/ChatService/ToolCalls/ToolCallStatusUpdater.swift‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct ToolCallStatusUpdater {
3939
AgentToolCall(
4040
id: toolCallId,
4141
name: toolCall.name,
42+
toolType: toolCall.toolType,
4243
status: newStatus
4344
),
4445
]
@@ -65,6 +66,7 @@ struct ToolCallStatusUpdater {
6566
AgentToolCall(
6667
id: toolCallId,
6768
name: toolCall.name,
69+
toolType: toolCall.toolType,
6870
status: newStatus
6971
),
7072
]

‎Core/Sources/ConversationTab/Views/BotMessage.swift‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ struct BotMessage: View {
127127
HStack{
128128
ifshouldShowTurnStatus(){
129129
TurnStatusView(message: message)
130+
.modify{ viewin
131+
if message.turnStatus==.inProgress{
132+
view
133+
.scaledPadding(.leading,6)
134+
}else{
135+
view
136+
}
137+
}
130138
}
131139

132140
Spacer()
@@ -256,8 +264,8 @@ private struct TurnStatusView: View {
256264
HStack(spacing:4){
257265
ProgressView()
258266
.controlSize(.small)
259-
.scaledFont(size: chatFontSize-1)
260-
.conditionalFontWeight(.medium)
267+
.scaledScaleEffect(0.7)
268+
.scaledFrame(width:16, height:16)
261269

262270
Text("Generating...")
263271
.scaledFont(size: chatFontSize-1)

‎Core/Sources/ConversationTab/Views/ConversationAgentProgressView/ConversationAgentProgressView.swift‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,12 @@ struct GenericToolTitleView: View {
156156
HStack(spacing:4){
157157
Text(toolStatus)
158158
.textSelection(.enabled)
159-
.scaledFont(size: chatFontSize, weight: fontWeight)
159+
.scaledFont(size: chatFontSize-1, weight: fontWeight)
160160
.foregroundStyle(.primary)
161161
.background(Color.clear)
162162
Text(toolName)
163163
.textSelection(.enabled)
164-
.scaledFont(size: chatFontSize, weight: fontWeight)
164+
.scaledFont(size: chatFontSize-1, weight: fontWeight)
165165
.foregroundStyle(.primary)
166166
.scaledPadding(.vertical,2)
167167
.scaledPadding(.horizontal,4)

‎Core/Sources/ConversationTab/Views/ConversationAgentProgressView/ToolStatusItemView.swift‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ struct ToolStatusItemView: View {
99
lettool:AgentToolCall
1010

1111
@AppStorage(\.chatFontSize)varchatFontSize
12+
@AppStorage(\.fontScale)varfontScale
1213

1314
@StateprivatevarisHoveringFileLink=false
1415

@@ -291,6 +292,31 @@ struct ToolStatusItemView: View {
291292
)
292293
}
293294

295+
@ViewBuilder
296+
func toolCallDetailSection(title:String, text:String)->someView{
297+
VStack(alignment:.leading, spacing:4){
298+
Text(title)
299+
.scaledFont(size: chatFontSize-1, weight:.medium)
300+
.foregroundColor(.secondary)
301+
markdownView(text: text)
302+
.toolCallDetailStyle(fontScale: fontScale)
303+
}
304+
}
305+
306+
varmcpDetailView:someView{
307+
VStack(alignment:.leading, spacing:8){
308+
iflet inputMessage= tool.inputMessage, !inputMessage.isEmpty{
309+
toolCallDetailSection(title:"Input", text: inputMessage)
310+
}
311+
iflet errorMessage= tool.error, !errorMessage.isEmpty{
312+
toolCallDetailSection(title:"Output", text: errorMessage)
313+
}
314+
iflet result= tool.result, !result.isEmpty{
315+
toolCallDetailSection(title:"Output", text: toolResultText??"")
316+
}
317+
}
318+
}
319+
294320
varprogress:someView{
295321
HStack(spacing:4){
296322
statusIcon
@@ -440,6 +466,11 @@ struct ToolStatusItemView: View {
440466
title: progress,
441467
content:markdownView(text:extractInsertEditContent(from: resultText))
442468
)
469+
}elseif tool.toolType==.mcp{
470+
ToolStatusDetailsView(
471+
title: progress,
472+
content: mcpDetailView
473+
)
443474
}elseif tool.status==.error{
444475
ToolStatusDetailsView(
445476
title: progress,
@@ -530,4 +561,20 @@ private extension View {
530561
}
531562
}
532563
}
564+
565+
func toolCallDetailStyle(fontScale:CGFloat)->someView{
566+
/// Leverage the `modify` extension to avoid refreshing of chat panel `List` view
567+
self.modify{ viewin
568+
view
569+
.foregroundColor(.secondary)
570+
.scaledPadding(4)
571+
.frame(maxWidth:.infinity, alignment:.leading)
572+
.background(SecondarySystemFillColor)
573+
.clipShape(RoundedRectangle(cornerRadius:6))
574+
.background(
575+
RoundedRectangle(cornerRadius:6)
576+
.stroke(Color.agentToolStatusOutlineColor, lineWidth:1* fontScale)
577+
)
578+
}
579+
}
533580
}

‎Core/Sources/Service/RealtimeSuggestionController.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ public actor RealtimeSuggestionController {
6969
lethandler={[weak self]in
7070
guardlet selfelse{return}
7171
awaitcancelInFlightTasks()
72-
awaitself.triggerPrefetchDebounced()
7372
awaitself.notifyEditingFileChange(editor: sourceEditor.element)
73+
awaitself.triggerPrefetchDebounced()
7474
}
7575

7676
forawait_in valueChange{

‎Tool/Sources/ChatAPIService/Memory/ChatMemory.swift‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,18 @@ public extension ChatMemory {
3535
fornewToolCallin messageToolCalls{
3636
iflet toolCallIndex= mergedToolCalls.firstIndex(where:{ $0.id== newToolCall.id}){
3737
mergedToolCalls[toolCallIndex].status= newToolCall.status
38+
iflet toolType= newToolCall.toolType{
39+
mergedToolCalls[toolCallIndex].toolType= toolType
40+
}
3841
iflet progressMessage= newToolCall.progressMessage, !progressMessage.isEmpty{
3942
mergedToolCalls[toolCallIndex].progressMessage= progressMessage
4043
}
44+
iflet input= newToolCall.input, !input.isEmpty{
45+
mergedToolCalls[toolCallIndex].input= input
46+
}
47+
iflet inputMessage= newToolCall.inputMessage, !inputMessage.isEmpty{
48+
mergedToolCalls[toolCallIndex].inputMessage= inputMessage
49+
}
4150
iflet result= newToolCall.result, !result.isEmpty{
4251
mergedToolCalls[toolCallIndex].result= result
4352
}
@@ -163,9 +172,18 @@ extension ChatMessage {
163172
fornewToolCallin newRound.toolCalls!{
164173
iflet toolCallIndex= mergedToolCalls.firstIndex(where:{ $0.id== newToolCall.id}){
165174
mergedToolCalls[toolCallIndex].status= newToolCall.status
175+
iflet toolType= newToolCall.toolType{
176+
mergedToolCalls[toolCallIndex].toolType= toolType
177+
}
166178
iflet progressMessage= newToolCall.progressMessage, !progressMessage.isEmpty{
167179
mergedToolCalls[toolCallIndex].progressMessage= newToolCall.progressMessage
168180
}
181+
iflet input= newToolCall.input, !input.isEmpty{
182+
mergedToolCalls[toolCallIndex].input= input
183+
}
184+
iflet inputMessage= newToolCall.inputMessage, !inputMessage.isEmpty{
185+
mergedToolCalls[toolCallIndex].inputMessage= inputMessage
186+
}
169187
iflet result= newToolCall.result, !result.isEmpty{
170188
mergedToolCalls[toolCallIndex].result= result
171189
}

‎Tool/Sources/ConversationServiceProvider/LSPTypes+AgentRound.swift‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import CopilotForXcodeKit
22
import Foundation
33
import LanguageServerProtocol
44

5-
65
publicstructAgentRound:Codable,Equatable{
76
publicletroundId:Int
87
publicvarreply:String
@@ -20,10 +19,11 @@ public struct AgentRound: Codable, Equatable {
2019
publicstructAgentToolCall:Codable,Equatable,Identifiable{
2120
publicletid:String
2221
publicletname:String
22+
publicvartoolType:ToolType?
2323
publicvarprogressMessage:String?
2424
publicvarstatus:ToolCallStatus
2525
publicvarinput:[String:AnyCodable]?
26-
publicletinputMessage:String?
26+
publicvarinputMessage:String?
2727
publicvarerror:String?
2828
publicvarresult:[ToolCallResultData]?
2929
publicvarresultDetails:[ToolResultItem]?
@@ -37,6 +37,7 @@ public struct AgentToolCall: Codable, Equatable, Identifiable {
3737
publicinit(
3838
id:String,
3939
name:String,
40+
toolType:ToolType?=nil,
4041
progressMessage:String?=nil,
4142
status:ToolCallStatus,
4243
input:[String:AnyCodable]?=nil,
@@ -49,6 +50,7 @@ public struct AgentToolCall: Codable, Equatable, Identifiable {
4950
){
5051
self.id= id
5152
self.name= name
53+
self.toolType= toolType
5254
self.progressMessage= progressMessage
5355
self.status= status
5456
self.input= input

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp