Skip to content

Commit 28a4bc3

Browse files
village-waydaniel-lxs
authored andcommitted
fix: async checkpoint initialization in background result to duplicate output in ChatView
Signed-off-by: wangdepeng <[email protected]>
1 parent 922f483 commit 28a4bc3

File tree

1 file changed

+63
-2
lines changed

1 file changed

+63
-2
lines changed

src/core/task/Task.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ import { ApiMessage } from "../task-persistence/apiMessages"
8686
import { getMessagesSinceLastSummary, summarizeConversation } from "../condense"
8787
import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
8888

89+
// Constants
90+
export const DEFAULT_CKPT_INIT_TIMEOUT_MS = 10000 // 10 seconds
91+
8992
export type ClineEvents = {
9093
message: [{ action: "created" | "updated"; message: ClineMessage }]
9194
taskStarted: []
@@ -105,6 +108,7 @@ export type TaskOptions = {
105108
apiConfiguration: ProviderSettings
106109
enableDiff?: boolean
107110
enableCheckpoints?: boolean
111+
checkpointInitTimeoutMs?: number
108112
fuzzyMatchThreshold?: number
109113
consecutiveMistakeLimit?: number
110114
task?: string
@@ -186,6 +190,7 @@ export class Task extends EventEmitter<ClineEvents> {
186190

187191
// Checkpoints
188192
enableCheckpoints: boolean
193+
checkpointInitTimeoutMs: number
189194
checkpointService?: RepoPerTaskCheckpointService
190195
checkpointServiceInitializing = false
191196

@@ -207,6 +212,7 @@ export class Task extends EventEmitter<ClineEvents> {
207212
apiConfiguration,
208213
enableDiff = false,
209214
enableCheckpoints = true,
215+
checkpointInitTimeoutMs = DEFAULT_CKPT_INIT_TIMEOUT_MS,
210216
fuzzyMatchThreshold = 1.0,
211217
consecutiveMistakeLimit = 3,
212218
task,
@@ -252,6 +258,7 @@ export class Task extends EventEmitter<ClineEvents> {
252258
this.globalStoragePath = provider.context.globalStorageUri.fsPath
253259
this.diffViewProvider = new DiffViewProvider(this.cwd)
254260
this.enableCheckpoints = enableCheckpoints
261+
this.checkpointInitTimeoutMs = checkpointInitTimeoutMs
255262

256263
this.rootTask = rootTask
257264
this.parentTask = parentTask
@@ -1108,8 +1115,8 @@ export class Task extends EventEmitter<ClineEvents> {
11081115
// Task Loop
11091116

11101117
private async initiateTaskLoop(userContent: Anthropic.Messages.ContentBlockParam[]): Promise<void> {
1111-
// Kicks off the checkpoints initialization process in the background.
1112-
getCheckpointService(this)
1118+
// Ensure checkpoint initialization completes before starting the main loop to prevent content duplication
1119+
await this.ensureCheckpointInitialization()
11131120

11141121
let nextUserContent = userContent
11151122
let includeFileDetails = true
@@ -1142,6 +1149,60 @@ export class Task extends EventEmitter<ClineEvents> {
11421149
}
11431150
}
11441151

1152+
/**
1153+
* Ensures checkpoint initialization completes before proceeding with task execution.
1154+
* This prevents content duplication issues where checkpoint_saved messages arrive
1155+
* after model streaming has already started.
1156+
*
1157+
* @param timeout - Configurable timeout in milliseconds (default: 10 seconds)
1158+
*/
1159+
private async ensureCheckpointInitialization(): Promise<void> {
1160+
if (!this.enableCheckpoints) {
1161+
return
1162+
}
1163+
1164+
const provider = this.providerRef.deref()
1165+
if (!provider) {
1166+
return
1167+
}
1168+
1169+
try {
1170+
// Start checkpoint service initialization
1171+
const service = getCheckpointService(this)
1172+
if (!service) {
1173+
return
1174+
}
1175+
1176+
// Wait for initialization to complete or timeout
1177+
await pWaitFor(
1178+
() => {
1179+
if (this.abort) {
1180+
return true // Exit early if task is aborted
1181+
}
1182+
return service.isInitialized || !this.enableCheckpoints
1183+
},
1184+
{
1185+
interval: 100,
1186+
timeout: this.checkpointInitTimeoutMs,
1187+
},
1188+
)
1189+
1190+
// If still initializing after timeout, disable checkpoints and continue
1191+
if (!service.isInitialized && this.enableCheckpoints) {
1192+
provider.log(
1193+
`[Task#ensureCheckpointInitialization] checkpoint initialization timed out after ${this.checkpointInitTimeoutMs}ms, disabling checkpoints`,
1194+
)
1195+
this.enableCheckpoints = false
1196+
}
1197+
} catch (error) {
1198+
// If checkpoint initialization fails, disable checkpoints and continue
1199+
provider?.log(
1200+
`[Task#ensureCheckpointInitialization] checkpoint initialization failed: ${error}, disabling checkpoints`,
1201+
)
1202+
this.enableCheckpoints = false
1203+
}
1204+
}
1205+
11451206
public async recursivelyMakeClineRequests(
11461207
userContent: Anthropic.Messages.ContentBlockParam[],
11471208
includeFileDetails: boolean = false,

0 commit comments

Comments
 (0)