@@ -10,10 +10,10 @@ import getFolderSize from "get-folder-size"
1010import * as path from "path"
1111import { serializeError } from "serialize-error"
1212import * as vscode from "vscode"
13- import { ApiHandler , SingleCompletionHandler , buildApiHandler } from "../api"
13+ import { ApiHandler , buildApiHandler } from "../api"
1414import { ApiStream } from "../api/transform/stream"
1515import { DIFF_VIEW_URI_SCHEME , DiffViewProvider } from "../integrations/editor/DiffViewProvider"
16- import { CheckpointService , CheckpointServiceFactory } from "../services/checkpoints"
16+ import { ShadowCheckpointService } from "../services/checkpoints/ShadowCheckpointService "
1717import { findToolName , formatContentBlockToMarkdown } from "../integrations/misc/export-markdown"
1818import {
1919 extractTextFromFile ,
@@ -116,7 +116,7 @@ export class Cline {
116116
117117 // checkpoints
118118 enableCheckpoints : boolean = false
119- private checkpointService ?: CheckpointService
119+ private checkpointService ?: ShadowCheckpointService
120120
121121 // streaming
122122 isWaitingForFirstChunk = false
@@ -747,8 +747,11 @@ export class Cline {
747747 }
748748
749749 private async initiateTaskLoop ( userContent : UserContent ) : Promise < void > {
750+ this . initializeCheckpoints ( )
751+
750752 let nextUserContent = userContent
751753 let includeFileDetails = true
754+
752755 while ( ! this . abort ) {
753756 const didEndLoop = await this . recursivelyMakeClineRequests ( nextUserContent , includeFileDetails )
754757 includeFileDetails = false // we only need file details the first time
@@ -2773,7 +2776,7 @@ export class Cline {
27732776 }
27742777
27752778 if ( isCheckpointPossible ) {
2776- await this . checkpointSave ( { isFirst : false } )
2779+ this . checkpointSave ( )
27772780 }
27782781
27792782 /*
@@ -2839,13 +2842,6 @@ export class Cline {
28392842 // get previous api req's index to check token usage and determine if we need to truncate conversation history
28402843 const previousApiReqIndex = findLastIndex ( this . clineMessages , ( m ) => m . say === "api_req_started" )
28412844
2842- // Save checkpoint if this is the first API request.
2843- const isFirstRequest = this . clineMessages . filter ( ( m ) => m . say === "api_req_started" ) . length === 0
2844-
2845- if ( isFirstRequest ) {
2846- await this . checkpointSave ( { isFirst : true } )
2847- }
2848-
28492845 // 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
28502846 // for the best UX we show a placeholder api_req_started message with a loading spinner as this happens
28512847 await this . say (
@@ -3356,37 +3352,88 @@ export class Cline {
33563352
33573353 // Checkpoints
33583354
3359- private async getCheckpointService ( ) {
3355+ private async initializeCheckpoints ( ) {
33603356 if ( ! this . enableCheckpoints ) {
3361- throw new Error ( "Checkpoints are disabled" )
3357+ return
33623358 }
33633359
3364- if ( ! this . checkpointService ) {
3360+ const log = ( message : string ) => {
3361+ console . log ( message )
3362+
3363+ try {
3364+ this . providerRef . deref ( ) ?. log ( message )
3365+ } catch ( err ) {
3366+ // NO-OP
3367+ }
3368+ }
3369+
3370+ try {
3371+ if ( this . checkpointService ) {
3372+ log ( "[Cline#initializeCheckpoints] checkpointService already initialized" )
3373+ return
3374+ }
3375+
33653376 const workspaceDir = vscode . workspace . workspaceFolders ?. map ( ( folder ) => folder . uri . fsPath ) . at ( 0 )
3366- const shadowDir = this . providerRef . deref ( ) ?. context . globalStorageUri . fsPath
33673377
33683378 if ( ! workspaceDir ) {
3369- this . providerRef . deref ( ) ?. log ( "[getCheckpointService] workspace folder not found" )
3370- throw new Error ( "Workspace directory not found" )
3379+ log ( "[Cline#initializeCheckpoints] workspace folder not found, disabling checkpoints" )
3380+ this . enableCheckpoints = false
3381+ return
33713382 }
33723383
3384+ const shadowDir = this . providerRef . deref ( ) ?. context . globalStorageUri . fsPath
3385+
33733386 if ( ! shadowDir ) {
3374- this . providerRef . deref ( ) ?. log ( "[getCheckpointService] shadowDir not found" )
3375- throw new Error ( "Global storage directory not found" )
3387+ log ( "[Cline#initializeCheckpoints] shadowDir not found, disabling checkpoints" )
3388+ this . enableCheckpoints = false
3389+ return
3390+ }
3391+
3392+ const service = await ShadowCheckpointService . create ( { taskId : this . taskId , workspaceDir, shadowDir, log } )
3393+
3394+ if ( ! service ) {
3395+ log ( "[Cline#initializeCheckpoints] failed to create checkpoint service, disabling checkpoints" )
3396+ this . enableCheckpoints = false
3397+ return
33763398 }
33773399
3378- this . checkpointService = await CheckpointServiceFactory . create ( {
3379- strategy : "shadow" ,
3380- options : {
3381- taskId : this . taskId ,
3382- workspaceDir,
3383- shadowDir,
3384- log : ( message ) => this . providerRef . deref ( ) ?. log ( message ) ,
3385- } ,
3400+ service . on ( "initialize" , ( { workspaceDir, created, duration } ) => {
3401+ try {
3402+ if ( created ) {
3403+ log ( `[Cline#initializeCheckpoints] created new shadow repo (${ workspaceDir } ) in ${ duration } ms` )
3404+ } else {
3405+ log (
3406+ `[Cline#initializeCheckpoints] found existing shadow repo (${ workspaceDir } ) in ${ duration } ms` ,
3407+ )
3408+ }
3409+
3410+ this . checkpointService = service
3411+ this . checkpointSave ( )
3412+ } catch ( err ) {
3413+ log ( "[Cline#initializeCheckpoints] caught error in on('initialize'), disabling checkpoints" )
3414+ this . enableCheckpoints = false
3415+ }
33863416 } )
3387- }
33883417
3389- return this . checkpointService
3418+ service . on ( "checkpoint" , ( { isFirst, fromHash : from , toHash : to } ) => {
3419+ try {
3420+ log ( `[Cline#initializeCheckpoints] ${ isFirst ? "initial" : "incremental" } checkpoint saved: ${ to } ` )
3421+ this . providerRef . deref ( ) ?. postMessageToWebview ( { type : "currentCheckpointUpdated" , text : to } )
3422+
3423+ this . say ( "checkpoint_saved" , to , undefined , undefined , { isFirst, from, to } ) . catch ( ( e ) =>
3424+ console . error ( "Error saving checkpoint message:" , e ) ,
3425+ )
3426+ } catch ( err ) {
3427+ log ( "[Cline#initializeCheckpoints] caught error in on('checkpoint'), disabling checkpoints" )
3428+ this . enableCheckpoints = false
3429+ }
3430+ } )
3431+
3432+ service . initShadowGit ( )
3433+ } catch ( err ) {
3434+ log ( "[Cline#initializeCheckpoints] caught error in initializeCheckpoints(), disabling checkpoints" )
3435+ this . enableCheckpoints = false
3436+ }
33903437 }
33913438
33923439 public async checkpointDiff ( {
@@ -3398,7 +3445,7 @@ export class Cline {
33983445 commitHash : string
33993446 mode : "full" | "checkpoint"
34003447 } ) {
3401- if ( ! this . enableCheckpoints ) {
3448+ if ( ! this . checkpointService || ! this . enableCheckpoints ) {
34023449 return
34033450 }
34043451
@@ -3414,8 +3461,7 @@ export class Cline {
34143461 }
34153462
34163463 try {
3417- const service = await this . getCheckpointService ( )
3418- const changes = await service . getDiff ( { from : previousCommitHash , to : commitHash } )
3464+ const changes = await this . checkpointService . getDiff ( { from : previousCommitHash , to : commitHash } )
34193465
34203466 if ( ! changes ?. length ) {
34213467 vscode . window . showInformationMessage ( "No changes found." )
@@ -3441,30 +3487,13 @@ export class Cline {
34413487 }
34423488 }
34433489
3444- public async checkpointSave ( { isFirst } : { isFirst : boolean } ) {
3445- if ( ! this . enableCheckpoints ) {
3490+ public checkpointSave ( ) {
3491+ if ( ! this . checkpointService || ! this . enableCheckpoints ) {
34463492 return
34473493 }
34483494
3449- try {
3450- const service = await this . getCheckpointService ( )
3451- const strategy = service . strategy
3452- const version = service . version
3453-
3454- const commit = await service . saveCheckpoint ( `Task: ${ this . taskId } , Time: ${ Date . now ( ) } ` )
3455- const fromHash = service . baseHash
3456- const toHash = isFirst ? commit ?. commit || fromHash : commit ?. commit
3457-
3458- if ( toHash ) {
3459- await this . providerRef . deref ( ) ?. postMessageToWebview ( { type : "currentCheckpointUpdated" , text : toHash } )
3460-
3461- const checkpoint = { isFirst, from : fromHash , to : toHash , strategy, version }
3462- await this . say ( "checkpoint_saved" , toHash , undefined , undefined , checkpoint )
3463- }
3464- } catch ( err ) {
3465- this . providerRef . deref ( ) ?. log ( "[checkpointSave] disabling checkpoints for this task" )
3466- this . enableCheckpoints = false
3467- }
3495+ // Start the checkpoint process in the background.
3496+ this . checkpointService . saveCheckpoint ( `Task: ${ this . taskId } , Time: ${ Date . now ( ) } ` )
34683497 }
34693498
34703499 public async checkpointRestore ( {
@@ -3476,7 +3505,7 @@ export class Cline {
34763505 commitHash : string
34773506 mode : "preview" | "restore"
34783507 } ) {
3479- if ( ! this . enableCheckpoints ) {
3508+ if ( ! this . checkpointService || ! this . enableCheckpoints ) {
34803509 return
34813510 }
34823511
@@ -3487,8 +3516,7 @@ export class Cline {
34873516 }
34883517
34893518 try {
3490- const service = await this . getCheckpointService ( )
3491- await service . restoreCheckpoint ( commitHash )
3519+ await this . checkpointService . restoreCheckpoint ( commitHash )
34923520
34933521 await this . providerRef . deref ( ) ?. postMessageToWebview ( { type : "currentCheckpointUpdated" , text : commitHash } )
34943522
0 commit comments