mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
subtasks alpha version (still in development)
This commit is contained in:
parent
79e5f56ccf
commit
c3c3874593
5 changed files with 218 additions and 78 deletions
|
|
@ -20,7 +20,7 @@ export const registerCommands = (options: RegisterCommandOptions) => {
|
|||
const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions) => {
|
||||
return {
|
||||
"roo-cline.plusButtonClicked": async () => {
|
||||
await provider.clearTask()
|
||||
await provider.removeClineFromStack()
|
||||
await provider.postStateToWebview()
|
||||
await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
|
||||
},
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@ type UserContent = Array<
|
|||
|
||||
export class Cline {
|
||||
readonly taskId: string
|
||||
// a flag that indicated if this Cline instance is a subtask (on finish return control to parent task)
|
||||
private isSubTask: boolean = false
|
||||
// a flag that indicated if this Cline instance is paused (waiting for provider to resume it after subtask completion)
|
||||
private isPaused: boolean = false
|
||||
api: ApiHandler
|
||||
private terminalManager: TerminalManager
|
||||
private urlContentFetcher: UrlContentFetcher
|
||||
|
|
@ -160,6 +164,12 @@ export class Cline {
|
|||
}
|
||||
}
|
||||
|
||||
// a helper function to set the private member isSubTask to true
|
||||
// and by that set this Cline instance to be a subtask (on finish return control to parent task)
|
||||
setSubTask() {
|
||||
this.isSubTask = true
|
||||
}
|
||||
|
||||
// Add method to update diffStrategy
|
||||
async updateDiffStrategy(experimentalDiffStrategy?: boolean) {
|
||||
// If not provided, get from current state
|
||||
|
|
@ -480,6 +490,43 @@ export class Cline {
|
|||
])
|
||||
}
|
||||
|
||||
async resumePausedTask() {
|
||||
// release this Cline instance from paused state
|
||||
this.isPaused = false
|
||||
|
||||
// Clear any existing ask state and simulate a completed ask response
|
||||
// this.askResponse = "messageResponse";
|
||||
// this.askResponseText = "Sub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task.";
|
||||
// this.askResponseImages = undefined;
|
||||
// this.lastMessageTs = Date.now();
|
||||
|
||||
// This adds the completion message to conversation history
|
||||
await this.say(
|
||||
"text",
|
||||
"Sub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task.",
|
||||
)
|
||||
|
||||
// this.userMessageContent.push({
|
||||
// type: "text",
|
||||
// text: `${"Result:\\n\\nSub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task."}`,
|
||||
// })
|
||||
|
||||
try {
|
||||
// Resume parent task
|
||||
await this.ask("resume_task")
|
||||
} catch (error) {
|
||||
if (error.message === "Current ask promise was ignored") {
|
||||
// ignore the ignored promise, since it was performed by launching a subtask and it probably took more then 1 sec,
|
||||
// also set the didAlreadyUseTool flag to indicate that the tool was already used, and there is no need to relaunch it
|
||||
this.didAlreadyUseTool = true
|
||||
} else {
|
||||
// Handle error appropriately
|
||||
console.error("Failed to resume task:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async resumeTaskFromHistory() {
|
||||
const modifiedClineMessages = await this.getSavedClineMessages()
|
||||
|
||||
|
|
@ -2553,10 +2600,12 @@ export class Cline {
|
|||
const provider = this.providerRef.deref()
|
||||
if (provider) {
|
||||
await provider.handleModeSwitch(mode)
|
||||
await provider.initClineWithTask(message)
|
||||
await provider.initClineWithSubTask(message)
|
||||
pushToolResult(
|
||||
`Successfully created new task in ${targetMode.name} mode with message: ${message}`,
|
||||
)
|
||||
// pasue the current task and start the new task
|
||||
this.isPaused = true
|
||||
} else {
|
||||
pushToolResult(
|
||||
formatResponse.toolError("Failed to create new task: provider not available"),
|
||||
|
|
@ -2648,6 +2697,10 @@ export class Cline {
|
|||
if (lastMessage && lastMessage.ask !== "command") {
|
||||
// havent sent a command message yet so first send completion_result then command
|
||||
await this.say("completion_result", result, undefined, false)
|
||||
if (this.isSubTask) {
|
||||
// tell the provider to remove the current subtask and resume the previous task in the stack
|
||||
this.providerRef.deref()?.finishSubTask()
|
||||
}
|
||||
}
|
||||
|
||||
// complete command message
|
||||
|
|
@ -2665,6 +2718,10 @@ export class Cline {
|
|||
commandResult = execCommandResult
|
||||
} else {
|
||||
await this.say("completion_result", result, undefined, false)
|
||||
if (this.isSubTask) {
|
||||
// tell the provider to remove the current subtask and resume the previous task in the stack
|
||||
this.providerRef.deref()?.finishSubTask()
|
||||
}
|
||||
}
|
||||
|
||||
// we already sent completion_result says, an empty string asks relinquishes control over button and field
|
||||
|
|
@ -2740,6 +2797,20 @@ export class Cline {
|
|||
}
|
||||
}
|
||||
|
||||
// this function checks if this Cline instance is set to pause state and wait for being resumed,
|
||||
// this is used when a sub-task is launched and the parent task is waiting for it to finish
|
||||
async waitForResume() {
|
||||
// wait until isPaused is false
|
||||
await new Promise<void>((resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
if (!this.isPaused) {
|
||||
clearInterval(interval)
|
||||
resolve()
|
||||
}
|
||||
}, 1000) // TBD: the 1 sec should be added to the settings, also should add a timeout to prevent infinit wait
|
||||
})
|
||||
}
|
||||
|
||||
async recursivelyMakeClineRequests(
|
||||
userContent: UserContent,
|
||||
includeFileDetails: boolean = false,
|
||||
|
|
@ -2779,6 +2850,12 @@ export class Cline {
|
|||
await this.checkpointSave({ isFirst: true })
|
||||
}
|
||||
|
||||
// in this Cline request loop, we need to check if this cline (Task) instance has been asked to wait
|
||||
// for a sub-task (it has launched) to finish before continuing
|
||||
if (this.isPaused) {
|
||||
await this.waitForResume()
|
||||
}
|
||||
|
||||
// getting verbose details is an expensive operation, it uses globby to top-down build file structure of project which for large projects can take a few seconds
|
||||
// for the best UX we show a placeholder api_req_started message with a loading spinner as this happens
|
||||
await this.say(
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
private disposables: vscode.Disposable[] = []
|
||||
private view?: vscode.WebviewView | vscode.WebviewPanel
|
||||
private isViewLaunched = false
|
||||
private cline?: Cline
|
||||
private clineStack: Cline[] = []
|
||||
private workspaceTracker?: WorkspaceTracker
|
||||
protected mcpHub?: McpHub // Change from private to protected
|
||||
private latestAnnouncementId = "jan-21-2025-custom-modes" // update to some unique identifier when we add a new announcement
|
||||
|
|
@ -176,6 +176,55 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
})
|
||||
}
|
||||
|
||||
// Adds a new Cline instance to clineStack, marking the start of a new task.
|
||||
// The instance is pushed to the top of the stack (LIFO order).
|
||||
// When the task is completed, the top instance is removed, reactivating the previous task.
|
||||
addClineToStack(cline: Cline): void {
|
||||
this.clineStack.push(cline)
|
||||
}
|
||||
|
||||
// Removes and destroys the top Cline instance (the current finished task), activating the previous one (resuming the parent task).
|
||||
async removeClineFromStack() {
|
||||
// pop the top Cline instance from the stack
|
||||
var clineToBeRemoved = this.clineStack.pop()
|
||||
if (clineToBeRemoved) {
|
||||
await clineToBeRemoved.abortTask()
|
||||
// make sure no reference kept, once promises end it will be garbage collected
|
||||
clineToBeRemoved = undefined
|
||||
}
|
||||
}
|
||||
|
||||
// remove the cline object with the received clineId, and all the cline objects bove it in the stack
|
||||
// for each cline object removed, pop it from the stack, abort the task and set it to undefined
|
||||
async removeClineWithIdFromStack(clineId: string) {
|
||||
const index = this.clineStack.findIndex((c) => c.taskId === clineId)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
for (let i = this.clineStack.length - 1; i >= index; i--) {
|
||||
this.removeClineFromStack()
|
||||
}
|
||||
}
|
||||
|
||||
// returns the current cline object in the stack (the top one)
|
||||
// if the stack is empty, returns undefined
|
||||
getCurrentCline(): Cline | undefined {
|
||||
if (this.clineStack.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
return this.clineStack[this.clineStack.length - 1]
|
||||
}
|
||||
|
||||
// remove the current task/cline instance (at the top of the stack), ao this task is finished
|
||||
// and resume the previous task/cline instance (if it exists)
|
||||
// this is used when a sub task is finished and the parent task needs to be resumed
|
||||
async finishSubTask() {
|
||||
// remove the last cline instance from the stack (this is the finished sub task)
|
||||
await this.removeClineFromStack()
|
||||
// resume the last cline instance in the stack (if it exists - this is the 'parnt' calling task)
|
||||
this.getCurrentCline()?.resumePausedTask()
|
||||
}
|
||||
|
||||
/*
|
||||
VSCode extensions use the disposable pattern to clean up resources when the sidebar/editor tab is closed by the user or system. This applies to event listening, commands, interacting with the UI, etc.
|
||||
- https://vscode-docs.readthedocs.io/en/stable/extensions/patterns-and-principles/
|
||||
|
|
@ -183,7 +232,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
*/
|
||||
async dispose() {
|
||||
this.outputChannel.appendLine("Disposing ClineProvider...")
|
||||
await this.clearTask()
|
||||
await this.removeClineFromStack()
|
||||
this.outputChannel.appendLine("Cleared task")
|
||||
if (this.view && "dispose" in this.view) {
|
||||
this.view.dispose()
|
||||
|
|
@ -236,7 +285,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
return false
|
||||
}
|
||||
|
||||
if (visibleProvider.cline) {
|
||||
// check if there is a cline instance in the stack (if this provider has an active task)
|
||||
if (visibleProvider.getCurrentCline()) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -267,7 +317,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
return
|
||||
}
|
||||
|
||||
if (visibleProvider.cline && command.endsWith("InCurrentTask")) {
|
||||
if (visibleProvider.getCurrentCline() && command.endsWith("InCurrentTask")) {
|
||||
await visibleProvider.postMessageToWebview({
|
||||
type: "invoke",
|
||||
invoke: "sendMessage",
|
||||
|
|
@ -303,7 +353,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
return
|
||||
}
|
||||
|
||||
if (visibleProvider.cline && command.endsWith("InCurrentTask")) {
|
||||
if (visibleProvider.getCurrentCline() && command.endsWith("InCurrentTask")) {
|
||||
await visibleProvider.postMessageToWebview({
|
||||
type: "invoke",
|
||||
invoke: "sendMessage",
|
||||
|
|
@ -392,13 +442,21 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
)
|
||||
|
||||
// if the extension is starting a new session, clear previous task state
|
||||
this.clearTask()
|
||||
await this.removeClineFromStack()
|
||||
|
||||
this.outputChannel.appendLine("Webview view resolved")
|
||||
}
|
||||
|
||||
// a wrapper that inits a new Cline instance (Task) ans setting it as a sub task of the current task
|
||||
public async initClineWithSubTask(task?: string, images?: string[]) {
|
||||
await this.initClineWithTask(task, images)
|
||||
this.getCurrentCline()?.setSubTask()
|
||||
}
|
||||
|
||||
// when initializing a new task, (not from history but from a tool command new_task) there is no need to remove the previouse task
|
||||
// since the new task is a sub task of the previous one, and when it finishes it is removed from the stack and the caller is resumed
|
||||
// in this way we can have a chain of tasks, each one being a sub task of the previous one until the main task is finished
|
||||
public async initClineWithTask(task?: string, images?: string[]) {
|
||||
await this.clearTask()
|
||||
const {
|
||||
apiConfiguration,
|
||||
customModePrompts,
|
||||
|
|
@ -413,7 +471,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
const modePrompt = customModePrompts?.[mode] as PromptComponent
|
||||
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
|
||||
|
||||
this.cline = new Cline(
|
||||
const newCline = new Cline(
|
||||
this,
|
||||
apiConfiguration,
|
||||
effectiveInstructions,
|
||||
|
|
@ -425,10 +483,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
undefined,
|
||||
experiments,
|
||||
)
|
||||
this.addClineToStack(newCline)
|
||||
}
|
||||
|
||||
public async initClineWithHistoryItem(historyItem: HistoryItem) {
|
||||
await this.clearTask()
|
||||
await this.removeClineFromStack()
|
||||
|
||||
const {
|
||||
apiConfiguration,
|
||||
|
|
@ -444,7 +503,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
const modePrompt = customModePrompts?.[mode] as PromptComponent
|
||||
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
|
||||
|
||||
this.cline = new Cline(
|
||||
const newCline = new Cline(
|
||||
this,
|
||||
apiConfiguration,
|
||||
effectiveInstructions,
|
||||
|
|
@ -456,6 +515,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
historyItem,
|
||||
experiments,
|
||||
)
|
||||
this.addClineToStack(newCline)
|
||||
}
|
||||
|
||||
public async postMessageToWebview(message: ExtensionMessage) {
|
||||
|
|
@ -810,11 +870,15 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await this.postStateToWebview()
|
||||
break
|
||||
case "askResponse":
|
||||
this.cline?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)
|
||||
this.getCurrentCline()?.handleWebviewAskResponse(
|
||||
message.askResponse!,
|
||||
message.text,
|
||||
message.images,
|
||||
)
|
||||
break
|
||||
case "clearTask":
|
||||
// newTask will start a new task with a given task text, while clear task resets the current session and allows for a new task to be started
|
||||
await this.clearTask()
|
||||
await this.removeClineFromStack()
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "didShowAnnouncement":
|
||||
|
|
@ -826,7 +890,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await this.postMessageToWebview({ type: "selectedImages", images })
|
||||
break
|
||||
case "exportCurrentTask":
|
||||
const currentTaskId = this.cline?.taskId
|
||||
const currentTaskId = this.getCurrentCline()?.taskId
|
||||
if (currentTaskId) {
|
||||
this.exportTaskWithId(currentTaskId)
|
||||
}
|
||||
|
|
@ -892,7 +956,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
const result = checkoutDiffPayloadSchema.safeParse(message.payload)
|
||||
|
||||
if (result.success) {
|
||||
await this.cline?.checkpointDiff(result.data)
|
||||
await this.getCurrentCline()?.checkpointDiff(result.data)
|
||||
}
|
||||
|
||||
break
|
||||
|
|
@ -903,13 +967,13 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await this.cancelTask()
|
||||
|
||||
try {
|
||||
await pWaitFor(() => this.cline?.isInitialized === true, { timeout: 3_000 })
|
||||
await pWaitFor(() => this.getCurrentCline()?.isInitialized === true, { timeout: 3_000 })
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage("Timed out when attempting to restore checkpoint.")
|
||||
}
|
||||
|
||||
try {
|
||||
await this.cline?.checkpointRestore(result.data)
|
||||
await this.getCurrentCline()?.checkpointRestore(result.data)
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage("Failed to restore checkpoint.")
|
||||
}
|
||||
|
|
@ -1145,42 +1209,43 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
)
|
||||
if (
|
||||
(answer === "Just this message" || answer === "This and all subsequent messages") &&
|
||||
this.cline &&
|
||||
this.getCurrentCline() &&
|
||||
typeof message.value === "number" &&
|
||||
message.value
|
||||
) {
|
||||
const timeCutoff = message.value - 1000 // 1 second buffer before the message to delete
|
||||
const messageIndex = this.cline.clineMessages.findIndex(
|
||||
(msg) => msg.ts && msg.ts >= timeCutoff,
|
||||
)
|
||||
const apiConversationHistoryIndex = this.cline.apiConversationHistory.findIndex(
|
||||
const messageIndex = this.getCurrentCline()!.clineMessages.findIndex(
|
||||
(msg) => msg.ts && msg.ts >= timeCutoff,
|
||||
)
|
||||
const apiConversationHistoryIndex =
|
||||
this.getCurrentCline()?.apiConversationHistory.findIndex(
|
||||
(msg) => msg.ts && msg.ts >= timeCutoff,
|
||||
)
|
||||
|
||||
if (messageIndex !== -1) {
|
||||
const { historyItem } = await this.getTaskWithId(this.cline.taskId)
|
||||
const { historyItem } = await this.getTaskWithId(this.getCurrentCline()!.taskId)
|
||||
|
||||
if (answer === "Just this message") {
|
||||
// Find the next user message first
|
||||
const nextUserMessage = this.cline.clineMessages
|
||||
.slice(messageIndex + 1)
|
||||
const nextUserMessage = this.getCurrentCline()!
|
||||
.clineMessages.slice(messageIndex + 1)
|
||||
.find((msg) => msg.type === "say" && msg.say === "user_feedback")
|
||||
|
||||
// Handle UI messages
|
||||
if (nextUserMessage) {
|
||||
// Find absolute index of next user message
|
||||
const nextUserMessageIndex = this.cline.clineMessages.findIndex(
|
||||
const nextUserMessageIndex = this.getCurrentCline()!.clineMessages.findIndex(
|
||||
(msg) => msg === nextUserMessage,
|
||||
)
|
||||
// Keep messages before current message and after next user message
|
||||
await this.cline.overwriteClineMessages([
|
||||
...this.cline.clineMessages.slice(0, messageIndex),
|
||||
...this.cline.clineMessages.slice(nextUserMessageIndex),
|
||||
await this.getCurrentCline()!.overwriteClineMessages([
|
||||
...this.getCurrentCline()!.clineMessages.slice(0, messageIndex),
|
||||
...this.getCurrentCline()!.clineMessages.slice(nextUserMessageIndex),
|
||||
])
|
||||
} else {
|
||||
// If no next user message, keep only messages before current message
|
||||
await this.cline.overwriteClineMessages(
|
||||
this.cline.clineMessages.slice(0, messageIndex),
|
||||
await this.getCurrentCline()!.overwriteClineMessages(
|
||||
this.getCurrentCline()!.clineMessages.slice(0, messageIndex),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1188,30 +1253,36 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
if (apiConversationHistoryIndex !== -1) {
|
||||
if (nextUserMessage && nextUserMessage.ts) {
|
||||
// Keep messages before current API message and after next user message
|
||||
await this.cline.overwriteApiConversationHistory([
|
||||
...this.cline.apiConversationHistory.slice(
|
||||
await this.getCurrentCline()!.overwriteApiConversationHistory([
|
||||
...this.getCurrentCline()!.apiConversationHistory.slice(
|
||||
0,
|
||||
apiConversationHistoryIndex,
|
||||
),
|
||||
...this.cline.apiConversationHistory.filter(
|
||||
...this.getCurrentCline()!.apiConversationHistory.filter(
|
||||
(msg) => msg.ts && msg.ts >= nextUserMessage.ts,
|
||||
),
|
||||
])
|
||||
} else {
|
||||
// If no next user message, keep only messages before current API message
|
||||
await this.cline.overwriteApiConversationHistory(
|
||||
this.cline.apiConversationHistory.slice(0, apiConversationHistoryIndex),
|
||||
await this.getCurrentCline()!.overwriteApiConversationHistory(
|
||||
this.getCurrentCline()!.apiConversationHistory.slice(
|
||||
0,
|
||||
apiConversationHistoryIndex,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (answer === "This and all subsequent messages") {
|
||||
// Delete this message and all that follow
|
||||
await this.cline.overwriteClineMessages(
|
||||
this.cline.clineMessages.slice(0, messageIndex),
|
||||
await this.getCurrentCline()!.overwriteClineMessages(
|
||||
this.getCurrentCline()!.clineMessages.slice(0, messageIndex),
|
||||
)
|
||||
if (apiConversationHistoryIndex !== -1) {
|
||||
await this.cline.overwriteApiConversationHistory(
|
||||
this.cline.apiConversationHistory.slice(0, apiConversationHistoryIndex),
|
||||
await this.getCurrentCline()!.overwriteApiConversationHistory(
|
||||
this.getCurrentCline()!.apiConversationHistory.slice(
|
||||
0,
|
||||
apiConversationHistoryIndex,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1481,8 +1552,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await this.updateGlobalState("experiments", updatedExperiments)
|
||||
|
||||
// Update diffStrategy in current Cline instance if it exists
|
||||
if (message.values[EXPERIMENT_IDS.DIFF_STRATEGY] !== undefined && this.cline) {
|
||||
await this.cline.updateDiffStrategy(
|
||||
if (message.values[EXPERIMENT_IDS.DIFF_STRATEGY] !== undefined && this.getCurrentCline()) {
|
||||
await this.getCurrentCline()!.updateDiffStrategy(
|
||||
Experiments.isEnabled(updatedExperiments, EXPERIMENT_IDS.DIFF_STRATEGY),
|
||||
)
|
||||
}
|
||||
|
|
@ -1724,25 +1795,25 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
this.updateGlobalState("requestyModelInfo", requestyModelInfo),
|
||||
this.updateGlobalState("modelTemperature", modelTemperature),
|
||||
])
|
||||
if (this.cline) {
|
||||
this.cline.api = buildApiHandler(apiConfiguration)
|
||||
if (this.getCurrentCline()) {
|
||||
this.getCurrentCline()!.api = buildApiHandler(apiConfiguration)
|
||||
}
|
||||
}
|
||||
|
||||
async cancelTask() {
|
||||
if (this.cline) {
|
||||
const { historyItem } = await this.getTaskWithId(this.cline.taskId)
|
||||
this.cline.abortTask()
|
||||
if (this.getCurrentCline()) {
|
||||
const { historyItem } = await this.getTaskWithId(this.getCurrentCline()!.taskId)
|
||||
this.getCurrentCline()!.abortTask()
|
||||
|
||||
await pWaitFor(
|
||||
() =>
|
||||
this.cline === undefined ||
|
||||
this.cline.isStreaming === false ||
|
||||
this.cline.didFinishAbortingStream ||
|
||||
this.getCurrentCline()! === undefined ||
|
||||
this.getCurrentCline()!.isStreaming === false ||
|
||||
this.getCurrentCline()!.didFinishAbortingStream ||
|
||||
// If only the first chunk is processed, then there's no
|
||||
// need to wait for graceful abort (closes edits, browser,
|
||||
// etc).
|
||||
this.cline.isWaitingForFirstChunk,
|
||||
this.getCurrentCline()!.isWaitingForFirstChunk,
|
||||
{
|
||||
timeout: 3_000,
|
||||
},
|
||||
|
|
@ -1750,11 +1821,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
console.error("Failed to abort task")
|
||||
})
|
||||
|
||||
if (this.cline) {
|
||||
if (this.getCurrentCline()) {
|
||||
// 'abandoned' will prevent this Cline instance from affecting
|
||||
// future Cline instances. This may happen if its hanging on a
|
||||
// streaming request.
|
||||
this.cline.abandoned = true
|
||||
this.getCurrentCline()!.abandoned = true
|
||||
}
|
||||
|
||||
// Clears task again, so we need to abortTask manually above.
|
||||
|
|
@ -1765,8 +1836,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
async updateCustomInstructions(instructions?: string) {
|
||||
// User may be clearing the field
|
||||
await this.updateGlobalState("customInstructions", instructions || undefined)
|
||||
if (this.cline) {
|
||||
this.cline.customInstructions = instructions || undefined
|
||||
if (this.getCurrentCline()) {
|
||||
this.getCurrentCline()!.customInstructions = instructions || undefined
|
||||
}
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
|
|
@ -1980,8 +2051,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await this.updateGlobalState("apiProvider", openrouter)
|
||||
await this.storeSecret("openRouterApiKey", apiKey)
|
||||
await this.postStateToWebview()
|
||||
if (this.cline) {
|
||||
this.cline.api = buildApiHandler({ apiProvider: openrouter, openRouterApiKey: apiKey })
|
||||
if (this.getCurrentCline()) {
|
||||
this.getCurrentCline()!.api = buildApiHandler({ apiProvider: openrouter, openRouterApiKey: apiKey })
|
||||
}
|
||||
// await this.postMessageToWebview({ type: "action", action: "settingsButtonClicked" }) // bad ux if user is on welcome
|
||||
}
|
||||
|
|
@ -2012,8 +2083,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await this.updateGlobalState("apiProvider", glama)
|
||||
await this.storeSecret("glamaApiKey", apiKey)
|
||||
await this.postStateToWebview()
|
||||
if (this.cline) {
|
||||
this.cline.api = buildApiHandler({
|
||||
if (this.getCurrentCline()) {
|
||||
this.getCurrentCline()!.api = buildApiHandler({
|
||||
apiProvider: glama,
|
||||
glamaApiKey: apiKey,
|
||||
})
|
||||
|
|
@ -2295,7 +2366,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
}
|
||||
|
||||
async showTaskWithId(id: string) {
|
||||
if (id !== this.cline?.taskId) {
|
||||
if (id !== this.getCurrentCline()?.taskId) {
|
||||
// non-current task
|
||||
const { historyItem } = await this.getTaskWithId(id)
|
||||
await this.initClineWithHistoryItem(historyItem) // clears existing task
|
||||
|
|
@ -2309,8 +2380,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
}
|
||||
|
||||
async deleteTaskWithId(id: string) {
|
||||
if (id === this.cline?.taskId) {
|
||||
await this.clearTask()
|
||||
if (id === this.getCurrentCline()?.taskId) {
|
||||
await this.removeClineWithIdFromStack(id)
|
||||
}
|
||||
|
||||
const { taskDirPath, apiConversationHistoryFilePath, uiMessagesFilePath } = await this.getTaskWithId(id)
|
||||
|
|
@ -2434,10 +2505,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
alwaysAllowMcp: alwaysAllowMcp ?? false,
|
||||
alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false,
|
||||
uriScheme: vscode.env.uriScheme,
|
||||
currentTaskItem: this.cline?.taskId
|
||||
? (taskHistory || []).find((item) => item.id === this.cline?.taskId)
|
||||
currentTaskItem: this.getCurrentCline()?.taskId
|
||||
? (taskHistory || []).find((item) => item.id === this.getCurrentCline()?.taskId)
|
||||
: undefined,
|
||||
clineMessages: this.cline?.clineMessages || [],
|
||||
clineMessages: this.getCurrentCline()?.clineMessages || [],
|
||||
taskHistory: (taskHistory || [])
|
||||
.filter((item: HistoryItem) => item.ts && item.task)
|
||||
.sort((a: HistoryItem, b: HistoryItem) => b.ts - a.ts),
|
||||
|
|
@ -2472,11 +2543,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
}
|
||||
}
|
||||
|
||||
async clearTask() {
|
||||
this.cline?.abortTask()
|
||||
this.cline = undefined // removes reference to it, so once promises end it will be garbage collected
|
||||
}
|
||||
|
||||
// Caching mechanism to keep track of webview messages + API conversation history per provider instance
|
||||
|
||||
/*
|
||||
|
|
@ -2914,10 +2980,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
}
|
||||
await this.configManager.resetAllConfigs()
|
||||
await this.customModesManager.resetCustomModes()
|
||||
if (this.cline) {
|
||||
this.cline.abortTask()
|
||||
this.cline = undefined
|
||||
}
|
||||
await this.removeClineFromStack()
|
||||
await this.postStateToWebview()
|
||||
await this.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
|
||||
}
|
||||
|
|
@ -2935,7 +2998,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
}
|
||||
|
||||
get messages() {
|
||||
return this.cline?.clineMessages || []
|
||||
return this.getCurrentCline()?.clineMessages || []
|
||||
}
|
||||
|
||||
// Add public getter
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ describe("ClineProvider", () => {
|
|||
// @ts-ignore - accessing private property for testing
|
||||
provider.cline = { abortTask: mockAbortTask }
|
||||
|
||||
await provider.clearTask()
|
||||
await provider.removeClineFromStack()
|
||||
|
||||
expect(mockAbortTask).toHaveBeenCalled()
|
||||
// @ts-ignore - accessing private property for testing
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export function createClineAPI(outputChannel: vscode.OutputChannel, sidebarProvi
|
|||
|
||||
startNewTask: async (task?: string, images?: string[]) => {
|
||||
outputChannel.appendLine("Starting new task")
|
||||
await sidebarProvider.clearTask()
|
||||
await sidebarProvider.removeClineFromStack()
|
||||
await sidebarProvider.postStateToWebview()
|
||||
await sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
|
||||
await sidebarProvider.postMessageToWebview({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue