@@ -6,13 +6,14 @@ import delay from "delay"
66import fs from "fs/promises"
77import os from "os"
88import pWaitFor from "p-wait-for"
9+ import getFolderSize from "get-folder-size"
910import * as path from "path"
1011import { serializeError } from "serialize-error"
1112import * as vscode from "vscode"
1213import { ApiHandler , SingleCompletionHandler , buildApiHandler } from "../api"
1314import { ApiStream } from "../api/transform/stream"
1415import { DIFF_VIEW_URI_SCHEME , DiffViewProvider } from "../integrations/editor/DiffViewProvider"
15- import { CheckpointService } from "../services/checkpoints/CheckpointService "
16+ import { CheckpointService , CheckpointServiceFactory } from "../services/checkpoints"
1617import { findToolName , formatContentBlockToMarkdown } from "../integrations/misc/export-markdown"
1718import {
1819 extractTextFromFile ,
@@ -239,7 +240,8 @@ export class Cline {
239240
240241 private async saveClineMessages ( ) {
241242 try {
242- const filePath = path . join ( await this . ensureTaskDirectoryExists ( ) , GlobalFileNames . uiMessages )
243+ const taskDir = await this . ensureTaskDirectoryExists ( )
244+ const filePath = path . join ( taskDir , GlobalFileNames . uiMessages )
243245 await fs . writeFile ( filePath , JSON . stringify ( this . clineMessages ) )
244246 // combined as they are in ChatView
245247 const apiMetrics = getApiMetrics ( combineApiRequests ( combineCommandSequences ( this . clineMessages . slice ( 1 ) ) ) )
@@ -251,6 +253,17 @@ export class Cline {
251253 ( m ) => ! ( m . ask === "resume_task" || m . ask === "resume_completed_task" ) ,
252254 )
253255 ]
256+
257+ let taskDirSize = 0
258+
259+ try {
260+ taskDirSize = await getFolderSize . loose ( taskDir )
261+ } catch ( err ) {
262+ console . error (
263+ `[saveClineMessages] failed to get task directory size (${ taskDir } ): ${ err instanceof Error ? err . message : String ( err ) } ` ,
264+ )
265+ }
266+
254267 await this . providerRef . deref ( ) ?. updateTaskHistory ( {
255268 id : this . taskId ,
256269 ts : lastRelevantMessage . ts ,
@@ -260,6 +273,7 @@ export class Cline {
260273 cacheWrites : apiMetrics . totalCacheWrites ,
261274 cacheReads : apiMetrics . totalCacheReads ,
262275 totalCost : apiMetrics . totalCost ,
276+ size : taskDirSize ,
263277 } )
264278 } catch ( error ) {
265279 console . error ( "Failed to save cline messages:" , error )
@@ -2692,7 +2706,7 @@ export class Cline {
26922706 }
26932707
26942708 if ( isCheckpointPossible ) {
2695- await this . checkpointSave ( )
2709+ await this . checkpointSave ( { isFirst : false } )
26962710 }
26972711
26982712 /*
@@ -2762,7 +2776,7 @@ export class Cline {
27622776 const isFirstRequest = this . clineMessages . filter ( ( m ) => m . say === "api_req_started" ) . length === 0
27632777
27642778 if ( isFirstRequest ) {
2765- await this . checkpointSave ( )
2779+ await this . checkpointSave ( { isFirst : true } )
27662780 }
27672781
27682782 // 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
@@ -3255,11 +3269,32 @@ export class Cline {
32553269 // Checkpoints
32563270
32573271 private async getCheckpointService ( ) {
3272+ if ( ! this . checkpointsEnabled ) {
3273+ throw new Error ( "Checkpoints are disabled" )
3274+ }
3275+
32583276 if ( ! this . checkpointService ) {
3259- this . checkpointService = await CheckpointService . create ( {
3260- taskId : this . taskId ,
3261- baseDir : vscode . workspace . workspaceFolders ?. map ( ( folder ) => folder . uri . fsPath ) . at ( 0 ) ?? "" ,
3262- log : ( message ) => this . providerRef . deref ( ) ?. log ( message ) ,
3277+ const workspaceDir = vscode . workspace . workspaceFolders ?. map ( ( folder ) => folder . uri . fsPath ) . at ( 0 )
3278+ const shadowDir = this . providerRef . deref ( ) ?. context . globalStorageUri . fsPath
3279+
3280+ if ( ! workspaceDir ) {
3281+ this . providerRef . deref ( ) ?. log ( "[getCheckpointService] workspace folder not found" )
3282+ throw new Error ( "Workspace directory not found" )
3283+ }
3284+
3285+ if ( ! shadowDir ) {
3286+ this . providerRef . deref ( ) ?. log ( "[getCheckpointService] shadowDir not found" )
3287+ throw new Error ( "Global storage directory not found" )
3288+ }
3289+
3290+ this . checkpointService = await CheckpointServiceFactory . create ( {
3291+ strategy : "shadow" ,
3292+ options : {
3293+ taskId : this . taskId ,
3294+ workspaceDir,
3295+ shadowDir,
3296+ log : ( message ) => this . providerRef . deref ( ) ?. log ( message ) ,
3297+ } ,
32633298 } )
32643299 }
32653300
@@ -3318,29 +3353,25 @@ export class Cline {
33183353 }
33193354 }
33203355
3321- public async checkpointSave ( ) {
3356+ public async checkpointSave ( { isFirst } : { isFirst : boolean } ) {
33223357 if ( ! this . checkpointsEnabled ) {
33233358 return
33243359 }
33253360
33263361 try {
3327- const isFirst = ! this . checkpointService
33283362 const service = await this . getCheckpointService ( )
3329- const commit = await service . saveCheckpoint ( `Task: ${ this . taskId } , Time: ${ Date . now ( ) } ` )
3363+ const strategy = service . strategy
3364+ const version = service . version
33303365
3331- if ( commit ?. commit ) {
3332- await this . providerRef
3333- . deref ( )
3334- ?. postMessageToWebview ( { type : "currentCheckpointUpdated" , text : service . currentCheckpoint } )
3366+ const commit = await service . saveCheckpoint ( `Task: ${ this . taskId } , Time: ${ Date . now ( ) } ` )
3367+ const fromHash = service . baseHash
3368+ const toHash = isFirst ? commit ?. commit || fromHash : commit ?. commit
33353369
3336- // Checkpoint metadata required by the UI.
3337- const checkpoint = {
3338- isFirst,
3339- from : service . baseCommitHash ,
3340- to : commit . commit ,
3341- }
3370+ if ( toHash ) {
3371+ await this . providerRef . deref ( ) ?. postMessageToWebview ( { type : "currentCheckpointUpdated" , text : toHash } )
33423372
3343- await this . say ( "checkpoint_saved" , commit . commit , undefined , undefined , checkpoint )
3373+ const checkpoint = { isFirst, from : fromHash , to : toHash , strategy, version }
3374+ await this . say ( "checkpoint_saved" , toHash , undefined , undefined , checkpoint )
33443375 }
33453376 } catch ( err ) {
33463377 this . providerRef . deref ( ) ?. log ( "[checkpointSave] disabling checkpoints for this task" )
@@ -3371,9 +3402,7 @@ export class Cline {
33713402 const service = await this . getCheckpointService ( )
33723403 await service . restoreCheckpoint ( commitHash )
33733404
3374- await this . providerRef
3375- . deref ( )
3376- ?. postMessageToWebview ( { type : "currentCheckpointUpdated" , text : service . currentCheckpoint } )
3405+ await this . providerRef . deref ( ) ?. postMessageToWebview ( { type : "currentCheckpointUpdated" , text : commitHash } )
33773406
33783407 if ( mode === "restore" ) {
33793408 await this . overwriteApiConversationHistory (
0 commit comments