-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat: Fork task from any chat message (#7904) #7905
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1557,6 +1557,59 @@ export class ClineProvider | |
| } | ||
| } | ||
|
|
||
| async forkTaskFromMessage(messageTs: number) { | ||
| const currentTask = this.getCurrentTask() | ||
| if (!currentTask) { | ||
| throw new Error("No active task to fork from") | ||
| } | ||
|
|
||
| // Find the message index | ||
| const messageIndex = currentTask.clineMessages.findIndex((msg) => msg.ts === messageTs) | ||
| if (messageIndex === -1) { | ||
| throw new Error("Message not found") | ||
| } | ||
|
|
||
| // Get messages up to and including the selected message | ||
| const messagesToCopy = currentTask.clineMessages.slice(0, messageIndex + 1) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validation: Consider validating that the messages to copy contain at least one user message to ensure the forked conversation is meaningful: const hasUserMessage = messagesToCopy.some(msg => msg.type === 'user')
if (!hasUserMessage) {
throw new Error("Cannot fork from this point: no user messages found")
} |
||
|
|
||
| // Create a new task with the copied conversation | ||
| const newTaskId = `${Date.now()}` | ||
| const historyItem: HistoryItem = { | ||
| id: newTaskId, | ||
| ts: Date.now(), | ||
| task: messagesToCopy[0]?.text || "Forked conversation", | ||
| mode: currentTask.taskMode || defaultModeSlug, | ||
| workspace: this.cwd, | ||
| number: (this.getGlobalState("taskHistory") ?? []).length + 1, | ||
| tokensIn: 0, | ||
| tokensOut: 0, | ||
| totalCost: 0, | ||
| } | ||
|
|
||
| // Save the new task to history | ||
| await this.updateTaskHistory(historyItem) | ||
|
|
||
| // Create the task directory and save messages | ||
| const { getTaskDirectoryPath } = await import("../../utils/storage") | ||
| const globalStoragePath = this.contextProxy.globalStorageUri.fsPath | ||
| const taskDirPath = await getTaskDirectoryPath(globalStoragePath, newTaskId) | ||
|
|
||
| // Ensure directory exists | ||
| await fs.mkdir(taskDirPath, { recursive: true }) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error Handling: File system operations should be wrapped in try-catch to handle potential failures gracefully: try {
await fs.mkdir(taskDirPath, { recursive: true })
await fs.writeFile(uiMessagesFilePath, JSON.stringify(messagesToCopy, null, 2))
} catch (error) {
console.error(`Failed to save forked task: ${error}`)
throw new Error(`Failed to create forked task: ${error.message}`)
} |
||
|
|
||
| // Save the messages to the new task | ||
| const uiMessagesFilePath = path.join(taskDirPath, GlobalFileNames.uiMessages) | ||
| await fs.writeFile(uiMessagesFilePath, JSON.stringify(messagesToCopy, null, 2)) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical Issue: The forked task is missing API conversation history preservation. While UI messages are saved, the corresponding // Also save API conversation history
const apiHistoryFilePath = path.join(taskDirPath, GlobalFileNames.apiConversationHistory)
const apiMessages = currentTask.apiConversationHistory.slice(0, messageIndex + 1)
await fs.writeFile(apiHistoryFilePath, JSON.stringify(apiMessages, null, 2)) |
||
|
|
||
| // Create and show the new forked task | ||
| await this.createTaskWithHistoryItem(historyItem) | ||
|
|
||
| // Show success message | ||
| vscode.window.showInformationMessage( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I18n: The success message is hardcoded in English. Consider using the i18n system for consistency: vscode.window.showInformationMessage(
t("fork.success_message")
) |
||
| "Task forked successfully. You can now continue the conversation from this point.", | ||
| ) | ||
| } | ||
|
|
||
| async deleteTaskFromState(id: string) { | ||
| const taskHistory = this.getGlobalState("taskHistory") ?? [] | ||
| const updatedTaskHistory = taskHistory.filter((task) => task.id !== id) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential Bug: Using
findIndexwith timestamp comparison could fail if multiple messages share the same timestamp (e.g., messages created in rapid succession). Consider adding a fallback or using a more unique identifier.