(
task: Task,
{ ts, commitHash, mode, operation = "delete" }: CheckpointRestoreOptions,
)
| 235 | } |
| 236 | |
| 237 | export async function checkpointRestore( |
| 238 | task: Task, |
| 239 | { ts, commitHash, mode, operation = "delete" }: CheckpointRestoreOptions, |
| 240 | ) { |
| 241 | const service = await getCheckpointService(task) |
| 242 | |
| 243 | if (!service) { |
| 244 | return |
| 245 | } |
| 246 | |
| 247 | const index = task.clineMessages.findIndex((m) => m.ts === ts) |
| 248 | |
| 249 | if (index === -1) { |
| 250 | return |
| 251 | } |
| 252 | |
| 253 | const provider = task.providerRef.deref() |
| 254 | |
| 255 | try { |
| 256 | await service.restoreCheckpoint(commitHash) |
| 257 | TelemetryService.instance.captureCheckpointRestored(task.taskId) |
| 258 | await provider?.postMessageToWebview({ type: "currentCheckpointUpdated", text: commitHash }) |
| 259 | |
| 260 | if (mode === "restore") { |
| 261 | // Calculate metrics from messages that will be deleted (must be done before rewind) |
| 262 | const deletedMessages = task.clineMessages.slice(index + 1) |
| 263 | |
| 264 | const { totalTokensIn, totalTokensOut, totalCacheWrites, totalCacheReads, totalCost } = getApiMetrics( |
| 265 | task.combineMessages(deletedMessages), |
| 266 | ) |
| 267 | |
| 268 | // Use MessageManager to properly handle context-management events |
| 269 | // This ensures orphaned Summary messages and truncation markers are cleaned up |
| 270 | await task.messageManager.rewindToTimestamp(ts, { |
| 271 | includeTargetMessage: operation === "edit", |
| 272 | }) |
| 273 | |
| 274 | // Report the deleted API request metrics |
| 275 | await task.say( |
| 276 | "api_req_deleted", |
| 277 | JSON.stringify({ |
| 278 | tokensIn: totalTokensIn, |
| 279 | tokensOut: totalTokensOut, |
| 280 | cacheWrites: totalCacheWrites, |
| 281 | cacheReads: totalCacheReads, |
| 282 | cost: totalCost, |
| 283 | } satisfies ClineApiReqInfo), |
| 284 | ) |
| 285 | } |
| 286 | |
| 287 | // The task is already cancelled by the provider beforehand, but we |
| 288 | // need to re-init to get the updated messages. |
| 289 | // |
| 290 | // This was taken from Cline's implementation of the checkpoints |
| 291 | // feature. The task instance will hang if we don't cancel twice, |
| 292 | // so this is currently necessary, but it seems like a complicated |
| 293 | // and hacky solution to a problem that I don't fully understand. |
| 294 | // I'd like to revisit this in the future and try to improve the |
no test coverage detected