-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Fix/task execution mode not preserved after interruption #3482
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
6164d2a
4764b1e
68c8ee1
61c9abc
8c84a1d
5d3d6bb
694bfd0
6716e41
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 |
|---|---|---|
|
|
@@ -113,6 +113,7 @@ export type TaskOptions = { | |
| parentTask?: Task | ||
| taskNumber?: number | ||
| onCreated?: (cline: Task) => void | ||
| currentModeSlug: string | ||
| } | ||
|
|
||
| export class Task extends EventEmitter<ClineEvents> { | ||
|
|
@@ -123,7 +124,8 @@ export class Task extends EventEmitter<ClineEvents> { | |
| readonly parentTask: Task | undefined = undefined | ||
| readonly taskNumber: number | ||
| readonly workspacePath: string | ||
|
|
||
| currentModeSlug: string | ||
| modeIsFrozen: boolean = false | ||
| providerRef: WeakRef<ClineProvider> | ||
| private readonly globalStoragePath: string | ||
| abort: boolean = false | ||
|
|
@@ -205,6 +207,7 @@ export class Task extends EventEmitter<ClineEvents> { | |
| parentTask, | ||
| taskNumber = -1, | ||
| onCreated, | ||
| currentModeSlug, | ||
| }: TaskOptions) { | ||
| super() | ||
|
|
||
|
|
@@ -239,6 +242,7 @@ export class Task extends EventEmitter<ClineEvents> { | |
| this.globalStoragePath = provider.context.globalStorageUri.fsPath | ||
| this.diffViewProvider = new DiffViewProvider(this.cwd) | ||
| this.enableCheckpoints = enableCheckpoints | ||
| this.currentModeSlug = currentModeSlug | ||
|
|
||
| this.rootTask = rootTask | ||
| this.parentTask = parentTask | ||
|
|
@@ -294,6 +298,17 @@ export class Task extends EventEmitter<ClineEvents> { | |
| await this.saveApiConversationHistory() | ||
| } | ||
|
|
||
| public async updateCurrentModeSlug(newModeSlug: string) { | ||
|
||
| if (this.modeIsFrozen) { | ||
| // If the mode is frozen, ignore any attempts to change it. | ||
| return // Crucial: Exit without updating the mode | ||
| } | ||
| // Only update if the new mode is actually different to avoid unnecessary assignments | ||
| if (this.currentModeSlug !== newModeSlug) { | ||
| this.currentModeSlug = newModeSlug | ||
| } | ||
| } | ||
|
|
||
| async overwriteApiConversationHistory(newHistory: ApiMessage[]) { | ||
| this.apiConversationHistory = newHistory | ||
| await this.saveApiConversationHistory() | ||
|
|
@@ -367,6 +382,7 @@ export class Task extends EventEmitter<ClineEvents> { | |
| taskNumber: this.taskNumber, | ||
| globalStoragePath: this.globalStoragePath, | ||
| workspace: this.cwd, | ||
| lastActiveModeSlug: this.currentModeSlug, | ||
| }) | ||
|
|
||
| this.emit("taskTokenUsageUpdated", this.taskId, tokenUsage) | ||
|
|
@@ -472,6 +488,14 @@ export class Task extends EventEmitter<ClineEvents> { | |
| await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text }) | ||
| } | ||
|
|
||
| // Set modeIsFrozen if the task just completed | ||
| if ( | ||
| (type === "completion_result" || type === "resume_completed_task") && | ||
| (partial === false || partial === undefined) | ||
| ) { | ||
| this.modeIsFrozen = true | ||
| } | ||
|
|
||
| await pWaitFor(() => this.askResponse !== undefined || this.lastMessageTs !== askTs, { interval: 100 }) | ||
|
|
||
| if (this.lastMessageTs !== askTs) { | ||
|
|
@@ -1069,6 +1093,20 @@ export class Task extends EventEmitter<ClineEvents> { | |
| throw new Error(`[RooCode#recursivelyMakeRooRequests] task ${this.taskId}.${this.instanceId} aborted`) | ||
| } | ||
|
|
||
| // Reset modeIsFrozen when a new message is sent | ||
| if (this.modeIsFrozen) { | ||
| this.modeIsFrozen = false | ||
| // Get the actual current mode from the provider and update the task's mode | ||
| const provider = this.providerRef.deref() | ||
| if (provider) { | ||
| const providerState = await provider.getState() | ||
| if (providerState.mode !== this.currentModeSlug) { | ||
| this.currentModeSlug = providerState.mode | ||
| } | ||
| } | ||
| await this.saveClineMessages() // Save messages now reflects the unfreezing and potential mode update | ||
| } | ||
|
|
||
| if (this.consecutiveMistakeCount >= this.consecutiveMistakeLimit) { | ||
| const { response, text, images } = await this.ask( | ||
| "mistake_limit_reached", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -192,6 +192,15 @@ export class ClineProvider | |
| } | ||
| } | ||
|
|
||
| public async clearStack() { | ||
|
||
| this.log("Clearing entire cline stack") | ||
| while (this.clineStack.length > 0) { | ||
| // removeClineFromStack already logs the removal of each task | ||
| await this.removeClineFromStack() | ||
| } | ||
| this.log("Cline stack cleared") | ||
| } | ||
|
||
|
|
||
| // returns the current cline object in the stack (the top one) | ||
| // if the stack is empty, returns undefined | ||
| getCurrentCline(): Task | undefined { | ||
|
|
@@ -217,8 +226,17 @@ export class ClineProvider | |
| console.log(`[subtasks] finishing subtask ${lastMessage}`) | ||
| // 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 'parent' calling task) | ||
| await this.getCurrentCline()?.resumePausedTask(lastMessage) | ||
|
|
||
| const parentCline = this.getCurrentCline() | ||
| if (parentCline) { | ||
| const parentLastActiveMode = parentCline.currentModeSlug // Changed from initialModeSlug | ||
| const currentActiveMode = (await this.getState()).mode | ||
|
|
||
| if (parentLastActiveMode && parentLastActiveMode !== currentActiveMode) { | ||
|
||
| await this.handleModeSwitch(parentLastActiveMode) | ||
| } | ||
| await parentCline.resumePausedTask(lastMessage) | ||
| } | ||
| } | ||
|
|
||
| /* | ||
|
|
@@ -517,6 +535,7 @@ export class ClineProvider | |
| diffEnabled: enableDiff, | ||
| enableCheckpoints, | ||
| fuzzyMatchThreshold, | ||
| mode, | ||
| experiments, | ||
| } = await this.getState() | ||
|
|
||
|
|
@@ -537,6 +556,7 @@ export class ClineProvider | |
| parentTask, | ||
| taskNumber: this.clineStack.length + 1, | ||
| onCreated: (cline) => this.emit("clineCreated", cline), | ||
| currentModeSlug: mode, | ||
| ...options, | ||
| }) | ||
|
|
||
|
|
@@ -552,6 +572,17 @@ export class ClineProvider | |
| public async initClineWithHistoryItem(historyItem: HistoryItem & { rootTask?: Task; parentTask?: Task }) { | ||
| await this.removeClineFromStack() | ||
|
|
||
| // Get current global mode first | ||
| let currentGlobalMode = (await this.getState()).mode | ||
|
|
||
| const targetModeSlug = historyItem.lastActiveModeSlug ?? currentGlobalMode | ||
|
|
||
| if (targetModeSlug !== currentGlobalMode) { | ||
| await this.handleModeSwitch(targetModeSlug) | ||
| // After switching, getState() will return the new active mode and its associated configs | ||
| } | ||
|
|
||
| // Re-fetch state after potential mode switch to get correct apiConfig, prompts, etc. | ||
| const { | ||
| apiConfiguration, | ||
| diffEnabled: enableDiff, | ||
|
|
@@ -571,6 +602,7 @@ export class ClineProvider | |
| rootTask: historyItem.rootTask, | ||
| parentTask: historyItem.parentTask, | ||
| taskNumber: historyItem.number, | ||
| currentModeSlug: targetModeSlug, // Pass the determined target mode slug | ||
| onCreated: (cline) => this.emit("clineCreated", cline), | ||
| }) | ||
|
|
||
|
|
@@ -773,10 +805,13 @@ export class ClineProvider | |
| * @param newMode The mode to switch to | ||
| */ | ||
| public async handleModeSwitch(newMode: Mode) { | ||
| const cline = this.getCurrentCline() | ||
| // Telemetry for mode switch (provider level) | ||
| const cline = this.getCurrentCline() // Get current cline for task ID if available | ||
| TelemetryService.instance.captureModeSwitch(cline?.taskId || "global", newMode) | ||
|
|
||
| if (cline) { | ||
| TelemetryService.instance.captureModeSwitch(cline.taskId, newMode) | ||
| await cline.updateCurrentModeSlug(newMode) | ||
| cline.emit("taskModeSwitched", cline.taskId, newMode) | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.