Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationAgent;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationCodeCopyParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationCreateParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationDestroyParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationMode;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationModesParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTemplate;
Expand Down Expand Up @@ -159,6 +160,12 @@ public interface CopilotLanguageServer extends LanguageServer {
@JsonRequest("conversation/persistence")
CompletableFuture<ChatPersistence> persistence(NullParams param);

/**
* Destroy a conversation, stopping any in-progress processing.
*/
@JsonRequest("conversation/destroy")
CompletableFuture<String> destroy(ConversationDestroyParams param);

/**
* Register agent tools to the language server.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationAgent;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationCodeCopyParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationCreateParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationDestroyParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationMode;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationModesParams;
import com.microsoft.copilot.eclipse.core.lsp.protocol.ConversationTemplate;
Expand Down Expand Up @@ -429,6 +430,21 @@ public CompletableFuture<ChatPersistence> persistence() {
});
}

/**
* Destroy a conversation, stopping any in-progress processing on the server.
*/
public void destroyConversation(String conversationId) {
if (StringUtils.isBlank(conversationId)) {
return;
}
Function<LanguageServer, CompletableFuture<String>> fn = server -> ((CopilotLanguageServer) server)
.destroy(new ConversationDestroyParams(conversationId));
this.languageServerWrapper.execute(fn).exceptionally(ex -> {
CopilotCore.LOGGER.error("Failed to destroy conversation: " + conversationId, ex);
return null;
});
}

/**
* Used to register the tools for the language server.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

package com.microsoft.copilot.eclipse.core.lsp.protocol;

import java.util.Objects;

import org.apache.commons.lang3.builder.ToStringBuilder;

/**
* Parameters for destroying a conversation.
*/
public class ConversationDestroyParams {
private String conversationId;

/**
* Creates a new ConversationDestroyParams.
*/
public ConversationDestroyParams(String conversationId) {
this.conversationId = conversationId;
}

public String getConversationId() {
return conversationId;
}

public void setConversationId(String conversationId) {
this.conversationId = conversationId;
}

@Override
public int hashCode() {
return Objects.hash(conversationId);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
ConversationDestroyParams other = (ConversationDestroyParams) obj;
return Objects.equals(conversationId, other.conversationId);
}

@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
builder.append("conversationId", conversationId);
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ public List<Turn> convertToTurns(List<AbstractTurnData> turnDataList) {
if (turnData == null) {
continue;
}
// Skip subagent turns - they are not part of the main conversation history
if (turnData instanceof CopilotTurnData copilotCheck
&& copilotCheck.getParentTurnId() != null) {
continue;
}
if (turnData instanceof UserTurnData userTurnData) {
String requestText = userTurnData.getMessage() != null ? userTurnData.getMessage().getText() : "";
Either<String, List<ChatCompletionContentPart>> request = Either
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@ private ConversationData updateConversationProgressInternal(String conversationI
CopilotTurnData copilotTurnData = findOrCreateCopilotTurn(conversationData, progress.getTurnId());
dataFactory.updateReplyFromProgress(copilotTurnData.getReply(), progress);

// Mark subagent turns with their parent turn ID
if (StringUtils.isNotBlank(progress.getParentTurnId())) {
copilotTurnData.setParentTurnId(progress.getParentTurnId());
}

// Update suggested title in CopilotTurnData if present
if (StringUtils.isNotBlank(progress.getSuggestedTitle())) {
copilotTurnData.setSuggestedTitle(progress.getSuggestedTitle());
Expand Down Expand Up @@ -543,4 +548,37 @@ public CompletableFuture<Void> persistModelInfo(String conversationId, String tu
public ConversationDataFactory getDataFactory() {
return dataFactory;
}

/**
* Sets the subagentToolCallId on a subagent's CopilotTurnData to associate it with the parent turn's run_subagent
* tool call. This enables precise positioning of subagent content during conversation restoration.
*
* @param conversationId the conversation ID
* @param subagentTurnId the subagent's turn ID
* @param toolCallId the run_subagent tool call ID from the parent turn
* @return a future that completes when the tool call ID has been set
*/
public CompletableFuture<Void> setSubagentToolCallId(String conversationId, String subagentTurnId,
String toolCallId) {
if (toolCallId == null || subagentTurnId == null) {
return CompletableFuture.completedFuture(null);
}
return CompletableFuture.runAsync(() -> {
lock.writeLock().lock();
try {
ConversationData conversation = getConversationFromCacheOrLoadFromDisk(conversationId);
if (conversation == null) {
return;
}
AbstractTurnData turnData = findTurn(conversation, subagentTurnId);
if (turnData instanceof CopilotTurnData turn && turn.getSubagentToolCallId() == null) {
turn.setSubagentToolCallId(toolCallId);
Comment thread
xinyi-gong marked this conversation as resolved.
}
} catch (IOException e) {
CopilotCore.LOGGER.error("Failed to set subagent tool call ID: " + conversationId, e);
} finally {
lock.writeLock().unlock();
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
public class CopilotTurnData extends AbstractTurnData {
private ReplyData reply;
private String suggestedTitle;
private String parentTurnId;
private String subagentToolCallId;

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

public String getParentTurnId() {
return parentTurnId;
}

public void setParentTurnId(String parentTurnId) {
this.parentTurnId = parentTurnId;
}

public String getSubagentToolCallId() {
return subagentToolCallId;
}

public void setSubagentToolCallId(String subagentToolCallId) {
this.subagentToolCallId = subagentToolCallId;
}

@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Objects.hash(reply, suggestedTitle);
result = prime * result + Objects.hash(reply, suggestedTitle, parentTurnId, subagentToolCallId);
return result;
}

Expand All @@ -63,7 +81,9 @@ public boolean equals(Object obj) {
return false;
}
CopilotTurnData other = (CopilotTurnData) obj;
return Objects.equals(reply, other.reply) && Objects.equals(suggestedTitle, other.suggestedTitle);
return Objects.equals(reply, other.reply) && Objects.equals(suggestedTitle, other.suggestedTitle)
&& Objects.equals(parentTurnId, other.parentTurnId)
&& Objects.equals(subagentToolCallId, other.subagentToolCallId);
}

@Override
Expand All @@ -77,6 +97,8 @@ public String toString() {
// Include CopilotTurnData specific properties
builder.append("reply", reply);
builder.append("suggestedTitle", suggestedTitle);
builder.append("parentTurnId", parentTurnId);
builder.append("subagentToolCallId", subagentToolCallId);
return builder.toString();
}

Expand Down
Loading
Loading