Skip to content

Commit b0265d9

Browse files
committed
fix subagent restoration related issues
1 parent 39565bc commit b0265d9

9 files changed

Lines changed: 541 additions & 58 deletions

File tree

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationAgent;
2626
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationCodeCopyParams;
2727
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationCreateParams;
28+
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationDestroyParams;
2829
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationMode;
2930
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationModesParams;
3031
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTemplate;
@@ -159,6 +160,12 @@ public interface CopilotLanguageServer extends LanguageServer {
159160
@JsonRequest("conversation/persistence")
160161
CompletableFuture<ChatPersistence> persistence(NullParams param);
161162

163+
/**
164+
* Destroy a conversation, stopping any in-progress processing.
165+
*/
166+
@JsonRequest("conversation/destroy")
167+
CompletableFuture<String> destroy(ConversationDestroyParams param);
168+
162169
/**
163170
* Register agent tools to the language server.
164171
*/

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageServerConnection.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationAgent;
4343
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationCodeCopyParams;
4444
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationCreateParams;
45+
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationDestroyParams;
4546
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationMode;
4647
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationModesParams;
4748
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTemplate;
@@ -429,6 +430,21 @@ public CompletableFuture<ChatPersistence> persistence() {
429430
});
430431
}
431432

433+
/**
434+
* Destroy a conversation, stopping any in-progress processing on the server.
435+
*/
436+
public void destroyConversation(String conversationId) {
437+
if (StringUtils.isBlank(conversationId)) {
438+
return;
439+
}
440+
Function<LanguageServer, CompletableFuture<String>> fn = server -> ((CopilotLanguageServer) server)
441+
.destroy(new ConversationDestroyParams(conversationId));
442+
this.languageServerWrapper.execute(fn).exceptionally(ex -> {
443+
CopilotCore.LOGGER.error("Failed to destroy conversation: " + conversationId, ex);
444+
return null;
445+
});
446+
}
447+
432448
/**
433449
* Used to register the tools for the language server.
434450
*/
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package com.microsoft.copilot.eclipse.core.lsp.protocol;
5+
6+
import java.util.Objects;
7+
8+
import org.apache.commons.lang3.builder.ToStringBuilder;
9+
10+
/**
11+
* Parameters for destroying a conversation.
12+
*/
13+
public class ConversationDestroyParams {
14+
private String conversationId;
15+
16+
/**
17+
* Creates a new ConversationDestroyParams.
18+
*/
19+
public ConversationDestroyParams(String conversationId) {
20+
this.conversationId = conversationId;
21+
}
22+
23+
public String getConversationId() {
24+
return conversationId;
25+
}
26+
27+
public void setConversationId(String conversationId) {
28+
this.conversationId = conversationId;
29+
}
30+
31+
@Override
32+
public int hashCode() {
33+
return Objects.hash(conversationId);
34+
}
35+
36+
@Override
37+
public boolean equals(Object obj) {
38+
if (this == obj) {
39+
return true;
40+
}
41+
if (obj == null || getClass() != obj.getClass()) {
42+
return false;
43+
}
44+
ConversationDestroyParams other = (ConversationDestroyParams) obj;
45+
return Objects.equals(conversationId, other.conversationId);
46+
}
47+
48+
@Override
49+
public String toString() {
50+
ToStringBuilder builder = new ToStringBuilder(this);
51+
builder.append("conversationId", conversationId);
52+
return builder.toString();
53+
}
54+
}

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/persistence/ConversationDataFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ public List<Turn> convertToTurns(List<AbstractTurnData> turnDataList) {
212212
if (turnData == null) {
213213
continue;
214214
}
215+
// Skip subagent turns - they are not part of the main conversation history
216+
if (turnData instanceof CopilotTurnData copilotCheck
217+
&& copilotCheck.getParentTurnId() != null) {
218+
continue;
219+
}
215220
if (turnData instanceof UserTurnData userTurnData) {
216221
String requestText = userTurnData.getMessage() != null ? userTurnData.getMessage().getText() : "";
217222
Either<String, List<ChatCompletionContentPart>> request = Either

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/persistence/ConversationPersistenceManager.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ private ConversationData updateConversationProgressInternal(String conversationI
286286
CopilotTurnData copilotTurnData = findOrCreateCopilotTurn(conversationData, progress.getTurnId());
287287
dataFactory.updateReplyFromProgress(copilotTurnData.getReply(), progress);
288288

289+
// Mark subagent turns with their parent turn ID
290+
if (StringUtils.isNotBlank(progress.getParentTurnId())) {
291+
copilotTurnData.setParentTurnId(progress.getParentTurnId());
292+
}
293+
289294
// Update suggested title in CopilotTurnData if present
290295
if (StringUtils.isNotBlank(progress.getSuggestedTitle())) {
291296
copilotTurnData.setSuggestedTitle(progress.getSuggestedTitle());
@@ -543,4 +548,35 @@ public CompletableFuture<Void> persistModelInfo(String conversationId, String tu
543548
public ConversationDataFactory getDataFactory() {
544549
return dataFactory;
545550
}
551+
552+
/**
553+
* Sets the subagentToolCallId on a subagent's CopilotTurnData to associate it with the parent turn's run_subagent
554+
* tool call. This enables precise positioning of subagent content during conversation restoration.
555+
*
556+
* @param conversationId the conversation ID
557+
* @param subagentTurnId the subagent's turn ID
558+
* @param toolCallId the run_subagent tool call ID from the parent turn
559+
*/
560+
public void setSubagentToolCallId(String conversationId, String subagentTurnId, String toolCallId) {
561+
if (toolCallId == null || subagentTurnId == null) {
562+
return;
563+
}
564+
CompletableFuture.runAsync(() -> {
565+
lock.writeLock().lock();
566+
try {
567+
ConversationData conversation = getConversationFromCacheOrLoadFromDisk(conversationId);
568+
if (conversation == null) {
569+
return;
570+
}
571+
AbstractTurnData turnData = findTurn(conversation, subagentTurnId);
572+
if (turnData instanceof CopilotTurnData turn && turn.getSubagentToolCallId() == null) {
573+
turn.setSubagentToolCallId(toolCallId);
574+
}
575+
} catch (IOException e) {
576+
CopilotCore.LOGGER.error("Failed to set subagent tool call ID: " + conversationId, e);
577+
} finally {
578+
lock.writeLock().unlock();
579+
}
580+
});
581+
}
546582
}

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/persistence/CopilotTurnData.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
public class CopilotTurnData extends AbstractTurnData {
2020
private ReplyData reply;
2121
private String suggestedTitle;
22+
private String parentTurnId;
23+
private String subagentToolCallId;
2224

2325
/**
2426
* Default constructor initializing default values.
@@ -43,11 +45,27 @@ public void setSuggestedTitle(String suggestedTitle) {
4345
this.suggestedTitle = suggestedTitle;
4446
}
4547

48+
public String getParentTurnId() {
49+
return parentTurnId;
50+
}
51+
52+
public void setParentTurnId(String parentTurnId) {
53+
this.parentTurnId = parentTurnId;
54+
}
55+
56+
public String getSubagentToolCallId() {
57+
return subagentToolCallId;
58+
}
59+
60+
public void setSubagentToolCallId(String subagentToolCallId) {
61+
this.subagentToolCallId = subagentToolCallId;
62+
}
63+
4664
@Override
4765
public int hashCode() {
4866
final int prime = 31;
4967
int result = super.hashCode();
50-
result = prime * result + Objects.hash(reply, suggestedTitle);
68+
result = prime * result + Objects.hash(reply, suggestedTitle, parentTurnId, subagentToolCallId);
5169
return result;
5270
}
5371

@@ -63,7 +81,9 @@ public boolean equals(Object obj) {
6381
return false;
6482
}
6583
CopilotTurnData other = (CopilotTurnData) obj;
66-
return Objects.equals(reply, other.reply) && Objects.equals(suggestedTitle, other.suggestedTitle);
84+
return Objects.equals(reply, other.reply) && Objects.equals(suggestedTitle, other.suggestedTitle)
85+
&& Objects.equals(parentTurnId, other.parentTurnId)
86+
&& Objects.equals(subagentToolCallId, other.subagentToolCallId);
6787
}
6888

6989
@Override
@@ -77,6 +97,8 @@ public String toString() {
7797
// Include CopilotTurnData specific properties
7898
builder.append("reply", reply);
7999
builder.append("suggestedTitle", suggestedTitle);
100+
builder.append("parentTurnId", parentTurnId);
101+
builder.append("subagentToolCallId", subagentToolCallId);
80102
return builder.toString();
81103
}
82104

0 commit comments

Comments
 (0)