Skip to content

Commit 12fbb8f

Browse files
committed
Pre-release 0.34.117
1 parent 677f73f commit 12fbb8f

File tree

73 files changed

+3487
-869
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+3487
-869
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"colors" : [
3+
{
4+
"color" : {
5+
"color-space" : "srgb",
6+
"components" : {
7+
"alpha" : "1.000",
8+
"blue" : "1.000",
9+
"green" : "1.000",
10+
"red" : "1.000"
11+
}
12+
},
13+
"idiom" : "universal"
14+
},
15+
{
16+
"appearances" : [
17+
{
18+
"appearance" : "luminosity",
19+
"value" : "dark"
20+
}
21+
],
22+
"color" : {
23+
"color-space" : "srgb",
24+
"components" : {
25+
"alpha" : "1.000",
26+
"blue" : "0.250",
27+
"green" : "0.250",
28+
"red" : "0.250"
29+
}
30+
},
31+
"idiom" : "universal"
32+
}
33+
],
34+
"info" : {
35+
"author" : "xcode",
36+
"version" : 1
37+
}
38+
}

Core/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ let package = Package(
131131
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
132132
.product(name: "KeyboardShortcuts", package: "KeyboardShortcuts"),
133133
.product(name: "GitHubCopilotService", package: "Tool"),
134+
.product(name: "Persist", package: "Tool"),
134135
]),
135136

136137
// MARK: - Suggestion Service

Core/Sources/ChatService/ChatService.swift

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
9595
subscribeToConversationContextRequest()
9696
subscribeToWatchedFilesHandler()
9797
subscribeToClientToolInvokeEvent()
98+
subscribeToClientToolConfirmationEvent()
9899
}
99100

100101
private func subscribeToNotifications() {
@@ -136,6 +137,27 @@ public final class ChatService: ChatServiceType, ObservableObject {
136137
}).store(in: &cancellables)
137138
}
138139

140+
private func subscribeToClientToolConfirmationEvent() {
141+
ClientToolHandlerImpl.shared.onClientToolConfirmationEvent.sink(receiveValue: { [weak self] (request, completion) in
142+
guard let params = request.params, params.conversationId == self?.conversationId else { return }
143+
let editAgentRounds: [AgentRound] = [
144+
AgentRound(roundId: params.roundId,
145+
reply: "",
146+
toolCalls: [
147+
AgentToolCall(id: params.toolCallId, name: params.name, status: .waitForConfirmation, invokeParams: params)
148+
]
149+
)
150+
]
151+
self?.appendToolCallHistory(turnId: params.turnId, editAgentRounds: editAgentRounds)
152+
self?.pendingToolCallRequests[params.toolCallId] = ToolCallRequest(
153+
requestId: request.id,
154+
turnId: params.turnId,
155+
roundId: params.roundId,
156+
toolCallId: params.toolCallId,
157+
completion: completion)
158+
}).store(in: &cancellables)
159+
}
160+
139161
private func subscribeToClientToolInvokeEvent() {
140162
ClientToolHandlerImpl.shared.onClientToolInvokeEvent.sink(receiveValue: { [weak self] (request, completion) in
141163
guard let params = request.params, params.conversationId == self?.conversationId else { return }
@@ -154,15 +176,7 @@ public final class ChatService: ChatServiceType, ObservableObject {
154176
return
155177
}
156178

157-
let completed = copilotTool.invokeTool(request, completion: completion, chatHistoryUpdater: self?.appendToolCallHistory, contextProvider: self)
158-
if !completed {
159-
self?.pendingToolCallRequests[params.toolCallId] = ToolCallRequest(
160-
requestId: request.id,
161-
turnId: params.turnId,
162-
roundId: params.roundId,
163-
toolCallId: params.toolCallId,
164-
completion: completion)
165-
}
179+
copilotTool.invokeTool(request, completion: completion, chatHistoryUpdater: self?.appendToolCallHistory, contextProvider: self)
166180
}).store(in: &cancellables)
167181
}
168182

@@ -225,11 +239,9 @@ public final class ChatService: ChatServiceType, ObservableObject {
225239
}
226240

227241
// Send the tool call result back to the server
228-
if let toolCallRequest = self.pendingToolCallRequests[toolCallId], status == .completed, let result = payload {
242+
if let toolCallRequest = self.pendingToolCallRequests[toolCallId], status == .accepted {
229243
self.pendingToolCallRequests.removeValue(forKey: toolCallId)
230-
let toolResult = LanguageModelToolResult(content: [
231-
.init(value: result)
232-
])
244+
let toolResult = LanguageModelToolConfirmationResult(result: .Accept)
233245
let jsonResult = try? JSONEncoder().encode(toolResult)
234246
let jsonValue = (try? JSONDecoder().decode(JSONValue.self, from: jsonResult ?? Data())) ?? JSONValue.null
235247
toolCallRequest.completion(
@@ -505,12 +517,27 @@ public final class ChatService: ChatServiceType, ObservableObject {
505517
private func handleProgressBegin(token: String, progress: ConversationProgressBegin) {
506518
guard let workDoneToken = activeRequestId, workDoneToken == token else { return }
507519
conversationId = progress.conversationId
520+
let turnId = progress.turnId
508521

509522
Task {
510523
if var lastUserMessage = await memory.history.last(where: { $0.role == .user }) {
511524
lastUserMessage.clsTurnID = progress.turnId
512525
saveChatMessageToStorage(lastUserMessage)
513526
}
527+
528+
/// Display an initial assistant message immediately after the user sends a message.
529+
/// This improves perceived responsiveness, especially in Agent Mode where the first
530+
/// ProgressReport may take long time.
531+
let message = ChatMessage(
532+
id: turnId,
533+
chatTabID: self.chatTabInfo.id,
534+
clsTurnID: turnId,
535+
role: .assistant,
536+
content: ""
537+
)
538+
539+
// will persist in resetOngoingRequest()
540+
await memory.appendMessage(message)
514541
}
515542
}
516543

@@ -642,17 +669,18 @@ public final class ChatService: ChatServiceType, ObservableObject {
642669
// cancel all pending tool call requests
643670
for (_, request) in pendingToolCallRequests {
644671
pendingToolCallRequests.removeValue(forKey: request.toolCallId)
645-
request.completion(AnyJSONRPCResponse(id: request.requestId,
646-
result: JSONValue.array([
647-
JSONValue.null,
648-
JSONValue.hash(
649-
[
650-
"code": .number(-32800), // client cancelled
651-
"message": .string("The client cancelled the tool call request \(request.toolCallId)")
652-
])
653-
])
654-
)
655-
)
672+
let toolResult = LanguageModelToolConfirmationResult(result: .Dismiss)
673+
let jsonResult = try? JSONEncoder().encode(toolResult)
674+
let jsonValue = (try? JSONDecoder().decode(JSONValue.self, from: jsonResult ?? Data())) ?? JSONValue.null
675+
request.completion(
676+
AnyJSONRPCResponse(
677+
id: request.requestId,
678+
result: JSONValue.array([
679+
jsonValue,
680+
JSONValue.null
681+
])
682+
)
683+
)
656684
}
657685

658686
Task {
@@ -901,7 +929,7 @@ extension [ChatMessage] {
901929
if index + 1 < count {
902930
let nextMessage = self[index + 1]
903931
if nextMessage.role == .assistant {
904-
turn.response = nextMessage.content
932+
turn.response = nextMessage.content + extractContentFromEditAgentRounds(nextMessage.editAgentRounds)
905933
index += 1
906934
}
907935
}
@@ -912,5 +940,14 @@ extension [ChatMessage] {
912940

913941
return turns
914942
}
943+
944+
private func extractContentFromEditAgentRounds(_ editAgentRounds: [AgentRound]) -> String {
945+
var content = ""
946+
for round in editAgentRounds {
947+
if !round.reply.isEmpty {
948+
content += round.reply
949+
}
950+
}
951+
return content
952+
}
915953
}
916-
Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,42 @@
11
import ConversationServiceProvider
2+
import Terminal
3+
import XcodeInspector
24
import JSONRPC
35

46
public class RunInTerminalTool: ICopilotTool {
57
public func invokeTool(_ request: InvokeClientToolRequest, completion: @escaping (AnyJSONRPCResponse) -> Void, chatHistoryUpdater: ChatHistoryUpdater?, contextProvider: (any ToolContextProvider)?) -> Bool {
68
let params = request.params!
7-
let editAgentRounds: [AgentRound] = [
8-
AgentRound(roundId: params.roundId,
9-
reply: "",
10-
toolCalls: [
11-
AgentToolCall(id: params.toolCallId, name: params.name, status: .waitForConfirmation, invokeParams: params)
12-
]
13-
)
14-
]
15-
16-
if let chatHistoryUpdater = chatHistoryUpdater {
17-
chatHistoryUpdater(params.turnId, editAgentRounds)
9+
10+
Task {
11+
var currentDirectory: String = ""
12+
if let workspacePath = contextProvider?.chatTabInfo.workspacePath,
13+
let xcodeIntance = Utils.getXcode(by: workspacePath) {
14+
currentDirectory = xcodeIntance.realtimeProjectURL?.path ?? xcodeIntance.projectRootURL?.path ?? ""
15+
} else {
16+
currentDirectory = await XcodeInspector.shared.safe.realtimeActiveProjectURL?.path ?? ""
17+
}
18+
if let input = params.input {
19+
let command = input["command"]?.value as? String
20+
let isBackground = input["isBackground"]?.value as? Bool
21+
let toolId = params.toolCallId
22+
let session = TerminalSessionManager.shared.createSession(for: toolId)
23+
if isBackground == true {
24+
session.executeCommand(
25+
currentDirectory: currentDirectory,
26+
command: command!) { result in
27+
// do nothing
28+
}
29+
completeResponse(request, response: "Command is running in terminal with ID=\(toolId)", completion: completion)
30+
} else {
31+
session.executeCommand(
32+
currentDirectory: currentDirectory,
33+
command: command!) { result in
34+
self.completeResponse(request, response: result.output, completion: completion)
35+
}
36+
}
37+
}
1838
}
1939

20-
return false
40+
return true
2141
}
2242
}

Core/Sources/ConversationTab/Chat.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ struct Chat {
9090
case downvote(MessageID, ConversationRating)
9191
case copyCode(MessageID)
9292
case insertCode(String)
93-
case toolCallStarted(String)
93+
case toolCallAccepted(String)
9494
case toolCallCompleted(String, String)
9595
case toolCallCancelled(String)
9696

@@ -182,10 +182,10 @@ struct Chat {
182182
try await service.send(id, content: message, skillSet: skillSet, references: selectedFiles, model: selectedModelFamily, agentMode: agentMode)
183183
}.cancellable(id: CancelID.sendMessage(self.id))
184184

185-
case let .toolCallStarted(toolCallId):
185+
case let .toolCallAccepted(toolCallId):
186186
guard !toolCallId.isEmpty else { return .none }
187187
return .run { _ in
188-
service.updateToolCallStatus(toolCallId: toolCallId, status: .running)
188+
service.updateToolCallStatus(toolCallId: toolCallId, status: .accepted)
189189
}.cancellable(id: CancelID.sendMessage(self.id))
190190
case let .toolCallCancelled(toolCallId):
191191
guard !toolCallId.isEmpty else { return .none }

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy