Repository navigation
fix(history): prompt on workspace mismatch #1660
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
base: main
Are you sure you want to change the base?
Changes from all commits
eb83c61
113996b
0964f34
5a28429
2e7054d
3ab8dbb
1f9f6d2
e73ca1d
bcccf98
e1d0944
6d5c790
6e7ca97
c47b767
6b75d6b
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -101,8 +101,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { fileExistsAtPath } from "../../utils/fs" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { setTtsEnabled, setTtsSpeed } from "../../utils/tts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getWorkspaceGitInfo } from "../../utils/git" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getWorkspacePath } from "../../utils/path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { arePathsEqual, getWorkspacePath } from "../../utils/path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { OrganizationAllowListViolationError } from "../../utils/errors" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getTaskDirectoryPath } from "../../utils/storage" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { setPanel } from "../../activate/registerCommands" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -131,7 +132,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interruptDelegatedChild, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LifecycleTransitionError, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "../task-persistence" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { readTaskMessages } from "../task-persistence/taskMessages" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { readTaskMessages, updateTaskMessages } from "../task-persistence/taskMessages" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getNonce } from "./getNonce" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getUri } from "./getUri" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { omitOriginalContentFromExtensionMessage } from "./stripOriginalContent" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2430,12 +2431,184 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (id !== this.getCurrentTask()?.taskId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Non-current task. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { historyItem } = await this.getTaskWithId(id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await this.createTaskWithHistoryItem(historyItem) // Clears existing task. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const preparedHistoryItem = await this.prepareHistoryItemForResume(historyItem) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!preparedHistoryItem) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await this.createTaskWithHistoryItem(preparedHistoryItem) // Clears existing task. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await this.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async prepareHistoryItemForResume<T extends HistoryItem>(historyItem: T): Promise<T | undefined> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const currentWorkspace = this.cwd | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const originalWorkspace = historyItem.workspace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!currentWorkspace || !originalWorkspace || arePathsEqual(currentWorkspace, originalWorkspace)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return historyItem | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // A live Task owns this history and would re-add checkpoint rows or rewrite its workspace after a reset. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.isTaskLiveInAnyProvider(historyItem.id)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return historyItem | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const useCurrentWorkspace = { title: "Use Current Workspace" } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const openOriginalWorkspace = { title: "Open Original Workspace" } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const selection = await vscode.window.showWarningMessage( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `This conversation was created in "${originalWorkspace}", but the current workspace is "${currentWorkspace}". ` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 2460 in src/core/webview/ClineProvider.ts
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Choose where to continue. Using the current workspace resets checkpoints created in the original workspace.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 2461 in src/core/webview/ClineProvider.ts
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { modal: true }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useCurrentWorkspace, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| openOriginalWorkspace, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (selection?.title === openOriginalWorkspace.title) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(originalWorkspace), { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| forceNewWindow: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (selection?.title !== useCurrentWorkspace.title) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2459
to
+2476
Contributor
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. 🔎 Supported by static analysis
Bypass the workspace prompt for CLI resume. The CLI sends The CLI does not call 🐛 Suggested fix
🧰 Tools
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The modal has no timeout, so the task may have been opened elsewhere while it was shown. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.isTaskLiveInAnyProvider(historyItem.id)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log(`[prepareHistoryItemForResume] Task ${historyItem.id} became live during workspace selection`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 2480 in src/core/webview/ClineProvider.ts
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updatedHistoryItem = { ...historyItem, workspace: currentWorkspace } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
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.
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. Good catch, fixed in c47b767. Both the forward write and the rollback now go through |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const persisted = await this.resetTaskCheckpointsForWorkspaceChange(historyItem, updatedHistoryItem) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Resume from the record as persisted now, not the snapshot read before the prompt. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { ...historyItem, ...persisted, workspace: currentWorkspace } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private isTaskLiveInAnyProvider(taskId: string): boolean { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ClineProvider.getAllInstances().some((provider) => provider.taskRegistry.getById(taskId) !== undefined) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Persist only `workspace`, read-modify-writing the stored record so fields that changed meanwhile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * (status, pendingAction, ...) are never overwritten from a stale snapshot. With `expectedWorkspace`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * the stored record is left unchanged unless it still has that workspace. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async setTaskHistoryWorkspace( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| historyItem: HistoryItem, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspace: string | undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expectedWorkspace?: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): Promise<HistoryItem | undefined> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const taskId = historyItem.id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.taskHistoryStore.get(taskId)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 2505 in src/core/webview/ClineProvider.ts
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Legacy fallback before the store is initialized: there is no stored record to preserve. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (expectedWorkspace !== undefined) return undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 2507 in src/core/webview/ClineProvider.ts
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await this.updateTaskHistory({ ...historyItem, workspace }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 2508 in src/core/webview/ClineProvider.ts
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.taskHistoryStore.get(taskId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let changed = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 2512 in src/core/webview/ClineProvider.ts
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await this.taskHistoryStore.atomicReadAndUpdate(taskId, (current) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (expectedWorkspace !== undefined && current.workspace !== expectedWorkspace) return current | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| changed = current.workspace !== workspace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { ...current, workspace } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2505
to
+2517
Contributor
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. 🔎 Supported by static analysis
Cover all The existing rollback test proves that a newer workspace is preserved, but it does not prove that the guarded rollback avoids a second The legacy fallback is reachable while startup is incomplete because
Additional assertion for the existing rollback test
🧰 Tools
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.recentTasksCache = undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const updated = this.taskHistoryStore.get(taskId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (changed && updated && this.isViewLaunched) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await this.postMessageToWebview({ type: "taskHistoryItemUpdated", taskHistoryItem: updated }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return updated | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async resetTaskCheckpointsForWorkspaceChange( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originalHistoryItem: HistoryItem, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updatedHistoryItem: HistoryItem, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): Promise<HistoryItem | undefined> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const taskId = originalHistoryItem.id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const globalStoragePath = this.contextProxy.globalStorageUri.fsPath | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let removedCheckpoints: ClineMessage[] = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let persisted: HistoryItem | undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const taskDir = await getTaskDirectoryPath(globalStoragePath, taskId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const checkpointsDir = path.join(taskDir, "checkpoints") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const checkpointBackupDir = path.join(taskDir, `checkpoints.workspace-change-${crypto.randomUUID()}`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let checkpointDirectoryStaged = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fs.rename(checkpointsDir, checkpointBackupDir) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checkpointDirectoryStaged = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await updateTaskMessages({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| taskId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| globalStoragePath, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| update: (messages) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| removedCheckpoints = messages.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (message) => message.type === "say" && message.say === "checkpoint_saved", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return messages.filter((message) => !removedCheckpoints.includes(message)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| persisted = await this.setTaskHistoryWorkspace(originalHistoryItem, updatedHistoryItem.workspace) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2548
to
+2559
Contributor
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. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
# Check whether migrated/legacy history items can lack ui_messages.json, and how Task resume handles a missing file.
rg -nP -C4 'migrateFromGlobalState\s*\(' src/core/task-persistence/TaskHistoryStore.ts
rg -nP -C4 'readTaskMessages\s*\(|not_found' src/core/task --type=ts -g '!**/__tests__/**'Repository: Zoo-Code-Org/Zoo-Code Length of output: 1048 🏁 Script executed: set -o pipefail
printf '%s\n' '--- ClineProvider reset and imports ---'
rg -n -C8 -F -- 'resetTaskCheckpointsForWorkspaceChange' src/core/webview/ClineProvider.ts
rg -n -C5 -F -- 'updateTaskMessages' src/core/webview/ClineProvider.ts
printf '%s\n' '--- persistence definitions and consumers ---'
rg -n -C8 -F -- 'function updateTaskMessages' src/core
rg -n -C8 -F -- 'const updateTaskMessages' src/core
rg -n -C8 -F -- 'function readTaskMessages' src/core
rg -n -C8 -F -- 'readTaskMessages' src/core/task-persistence src/core/task --glob '*.ts'
printf '%s\n' '--- migration implementation ---'
sed -n '790,875p' src/core/task-persistence/TaskHistoryStore.ts
printf '%s\n' '--- relevant file-name and task creation writes ---'
rg -n -C5 -F -- 'ui_messages.json' src/core --glob '*.ts'Repository: Zoo-Code-Org/Zoo-Code Length of output: 41852 🏁 Script executed: set -o pipefail
printf '%s\n' '--- exact reset block and imports ---'
nl -ba src/core/webview/ClineProvider.ts | sed -n '1,155p;2525,2610p'
printf '%s\n' '--- exact task message persistence implementation ---'
nl -ba src/core/task-persistence/taskMessages.ts | sed -n '1,125p'
printf '%s\n' '--- migration implementation ---'
nl -ba src/core/task-persistence/TaskHistoryStore.ts | sed -n '790,875p'
printf '%s\n' '--- history/task file creation paths ---'
rg -n -C6 -F -- 'saveTaskMessages' src/core --glob '*.ts'
rg -n -C6 -F -- 'migrateFromGlobalState' src/core --glob '*.ts'Repository: Zoo-Code-Org/Zoo-Code Length of output: 42338 🏁 Script executed: set -o pipefail
printf '%s\n' '--- historical resume entrypoints ---'
rg -n -C12 -F -- 'showTaskWithId' src/core/webview/ClineProvider.ts src/core --glob '*.ts'
rg -n -C12 -F -- 'prepareHistoryItemForResume' src/core --glob '*.ts'
rg -n -C10 -F -- 'resumeTask' src/core --glob '*.ts'
printf '%s\n' '--- task construction/message loading ---'
rg -n -C10 -F -- 'getSavedClineMessages' src/core/task/Task.ts
rg -n -C10 -F -- 'readTaskMessages({' src/core/webview/ClineProvider.ts src/core/task/Task.tsRepository: Zoo-Code-Org/Zoo-Code Length of output: 42765 Skip checkpoint-message cleanup when When History migration creates Suggested fix try {
- await updateTaskMessages({
- taskId,
- globalStoragePath,
- update: (messages) => {
- removedCheckpoints = messages.filter(
- (message) => message.type === "say" && message.say === "checkpoint_saved",
- )
- return messages.filter((message) => !removedCheckpoints.includes(message))
- },
- })
+ const messagesPath = path.join(taskDir, GlobalFileNames.uiMessages)
+ if (await fileExistsAtPath(messagesPath)) {
+ await updateTaskMessages({
+ taskId,
+ globalStoragePath,
+ update: (messages) => {
+ removedCheckpoints = messages.filter(
+ (message) => message.type === "say" && message.say === "checkpoint_saved",
+ )
+ return messages.filter((message) => !removedCheckpoints.includes(message))
+ },
+ })
+ }
persisted = await this.setTaskHistoryWorkspace(originalHistoryItem, updatedHistoryItem.workspace)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (removedCheckpoints.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Restore only the removed rows; never replace intervening message writes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await saveTaskMessages({ messages: removedCheckpoints, taskId, globalStoragePath, merge: true }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (rollbackError) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `[resetTaskCheckpointsForWorkspaceChange] Failed to restore messages for ${taskId}: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (checkpointDirectoryStaged) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fs.rename(checkpointBackupDir, checkpointsDir) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (rollbackError) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `[resetTaskCheckpointsForWorkspaceChange] Failed to restore checkpoints for ${taskId} from ${checkpointBackupDir}: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Revert only the workspace, and only if no newer write has moved it again. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await this.setTaskHistoryWorkspace( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originalHistoryItem, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originalHistoryItem.workspace, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updatedHistoryItem.workspace, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (rollbackError) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `[resetTaskCheckpointsForWorkspaceChange] Failed to restore history for ${taskId}: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (checkpointDirectoryStaged) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (let attempt = 0; attempt < 3; attempt++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fs.rm(checkpointBackupDir, { recursive: true, force: true }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `[resetTaskCheckpointsForWorkspaceChange] Failed to remove checkpoint backup ${checkpointBackupDir}: ${error instanceof Error ? error.message : String(error)}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (attempt < 2) await delay(100) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return persisted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async exportTaskWithId(id: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { historyItem, apiConversationHistory } = await this.getTaskWithId(id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fileName = getTaskFileName(historyItem.ts) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.