Skip to content

Commit 4ff1113

Browse files
committed
fix: address async mode initialization concerns
- Add taskModeReady promise to ensure async initialization completes - Add waitForModeInitialization() method for external callers - Improve error handling to use provider.log() instead of console.error - Add JSDoc documentation for taskMode property - Update tests to use waitForModeInitialization() instead of setTimeout
1 parent a397737 commit 4ff1113

File tree

2 files changed

+55
-28
lines changed

2 files changed

+55
-28
lines changed

src/core/task/Task.ts

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,16 @@ export class Task extends EventEmitter<ClineEvents> {
137137
readonly parentTask: Task | undefined = undefined
138138
readonly taskNumber: number
139139
readonly workspacePath: string
140+
/**
141+
* The mode associated with this task. Persisted across sessions
142+
* to maintain user context when reopening tasks from history.
143+
*/
140144
taskMode: string
145+
/**
146+
* Promise that resolves when the task mode has been initialized.
147+
* This ensures async mode initialization completes before the task is used.
148+
*/
149+
private taskModeReady: Promise<void>
141150

142151
providerRef: WeakRef<ClineProvider>
143152
private readonly globalStoragePath: string
@@ -279,22 +288,13 @@ export class Task extends EventEmitter<ClineEvents> {
279288
TelemetryService.instance.captureTaskCreated(this.taskId)
280289
}
281290

282-
// If no historyItem, get the current mode from provider
283-
if (!historyItem && provider.getState) {
284-
const statePromise = provider.getState()
285-
// Check if getState() returns a Promise
286-
if (statePromise && typeof statePromise.then === "function") {
287-
statePromise
288-
.then((state) => {
289-
if (state?.mode) {
290-
this.taskMode = state.mode
291-
}
292-
})
293-
.catch((error) => {
294-
// If there's an error getting state, keep the default mode
295-
console.error("Failed to get mode from provider state:", error)
296-
})
297-
}
291+
// Initialize the task mode ready promise
292+
if (!historyItem) {
293+
// For new tasks, initialize mode asynchronously
294+
this.taskModeReady = this.initializeTaskMode(provider)
295+
} else {
296+
// For history items, mode is already set
297+
this.taskModeReady = Promise.resolve()
298298
}
299299

300300
// Only set up diff strategy if diff is enabled
@@ -330,6 +330,32 @@ export class Task extends EventEmitter<ClineEvents> {
330330
}
331331
}
332332

333+
/**
334+
* Initialize the task mode from the provider state.
335+
* This method handles async initialization with proper error handling.
336+
*/
337+
private async initializeTaskMode(provider: ClineProvider): Promise<void> {
338+
try {
339+
const state = await provider.getState()
340+
if (state?.mode) {
341+
this.taskMode = state.mode
342+
}
343+
} catch (error) {
344+
// If there's an error getting state, keep the default mode
345+
// Use the provider's log method for better error visibility
346+
const errorMessage = `Failed to initialize task mode: ${error instanceof Error ? error.message : String(error)}`
347+
provider.log(errorMessage)
348+
}
349+
}
350+
351+
/**
352+
* Wait for the task mode to be initialized.
353+
* This should be called before any operations that depend on the correct mode being set.
354+
*/
355+
public async waitForModeInitialization(): Promise<void> {
356+
return this.taskModeReady
357+
}
358+
333359
static create(options: TaskOptions): [Task, Promise<void>] {
334360
const instance = new Task({ ...options, startTask: false })
335361
const { images, task, historyItem } = options

src/core/task/__tests__/Task.sticky-mode.spec.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,8 @@ describe("Task - Sticky Mode", () => {
283283
startTask: false,
284284
})
285285

286-
// Wait for async mode initialization
287-
await new Promise((resolve) => setTimeout(resolve, 10))
286+
// Wait for async mode initialization using the new method
287+
await task.waitForModeInitialization()
288288

289289
// Import taskMetadata mock
290290
const { taskMetadata } = await import("../../task-persistence")
@@ -313,8 +313,8 @@ describe("Task - Sticky Mode", () => {
313313
startTask: false,
314314
})
315315

316-
// Wait for async mode initialization
317-
await new Promise((resolve) => setTimeout(resolve, 10))
316+
// Wait for async mode initialization using the new method
317+
await task.waitForModeInitialization()
318318

319319
const { taskMetadata } = await import("../../task-persistence")
320320
vi.mocked(taskMetadata).mockClear()
@@ -385,8 +385,8 @@ describe("Task - Sticky Mode", () => {
385385
startTask: false,
386386
})
387387

388-
// Wait for async mode initialization
389-
await new Promise((resolve) => setTimeout(resolve, 10))
388+
// Wait for async mode initialization using the new method
389+
await parentTask.waitForModeInitialization()
390390

391391
// Change provider mode to code
392392
mockProvider.getState.mockResolvedValue({
@@ -403,8 +403,8 @@ describe("Task - Sticky Mode", () => {
403403
startTask: false,
404404
})
405405

406-
// Wait for async mode initialization
407-
await new Promise((resolve) => setTimeout(resolve, 10))
406+
// Wait for async mode initialization using the new method
407+
await childTask.waitForModeInitialization()
408408

409409
const { taskMetadata } = await import("../../task-persistence")
410410
vi.mocked(taskMetadata).mockClear()
@@ -447,8 +447,8 @@ describe("Task - Sticky Mode", () => {
447447
startTask: false,
448448
})
449449

450-
// Wait for async mode initialization
451-
await new Promise((resolve) => setTimeout(resolve, 10))
450+
// Wait for async mode initialization using the new method
451+
await task.waitForModeInitialization()
452452

453453
// Task should use defaultModeSlug when provider returns no mode
454454
expect((task as any).taskMode).toBe("architect")
@@ -475,6 +475,7 @@ describe("Task - Sticky Mode", () => {
475475
const errorProvider = {
476476
...mockProvider,
477477
getState: vi.fn().mockRejectedValue(new Error("Provider error")),
478+
log: vi.fn(), // Add the log method to the mock
478479
}
479480

480481
const task = new Task({
@@ -484,8 +485,8 @@ describe("Task - Sticky Mode", () => {
484485
startTask: false,
485486
})
486487

487-
// Wait a bit for the promise rejection to settle
488-
await new Promise((resolve) => setTimeout(resolve, 20))
488+
// Wait for the promise rejection to settle using the new method
489+
await task.waitForModeInitialization()
489490

490491
// Task should still be created without throwing
491492
expect(task).toBeDefined()

0 commit comments

Comments
 (0)