@@ -137,6 +137,49 @@ 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+ *
144+ * ## Lifecycle
145+ *
146+ * ### For new tasks:
147+ * 1. Initially `undefined` during construction
148+ * 2. Asynchronously initialized from provider state via `initializeTaskMode()`
149+ * 3. Falls back to `defaultModeSlug` if provider state is unavailable
150+ *
151+ * ### For history items:
152+ * 1. Immediately set from `historyItem.mode` during construction
153+ * 2. Falls back to `defaultModeSlug` if mode is not stored in history
154+ *
155+ * ## Important
156+ * This property should NOT be accessed directly until `taskModeReady` promise resolves.
157+ * Use `getTaskMode()` for async access or `taskMode` getter for sync access after initialization.
158+ *
159+ * @private
160+ * @see {@link getTaskMode } - For safe async access
161+ * @see {@link taskMode } - For sync access after initialization
162+ * @see {@link waitForModeInitialization } - To ensure initialization is complete
163+ */
164+ private _taskMode : string | undefined
165+
166+ /**
167+ * Promise that resolves when the task mode has been initialized.
168+ * This ensures async mode initialization completes before the task is used.
169+ *
170+ * ## Purpose
171+ * - Prevents race conditions when accessing task mode
172+ * - Ensures provider state is properly loaded before mode-dependent operations
173+ * - Provides a synchronization point for async initialization
174+ *
175+ * ## Resolution timing
176+ * - For history items: Resolves immediately (sync initialization)
177+ * - For new tasks: Resolves after provider state is fetched (async initialization)
178+ *
179+ * @private
180+ * @see {@link waitForModeInitialization } - Public method to await this promise
181+ */
182+ private taskModeReady : Promise < void >
140183
141184 providerRef : WeakRef < ClineProvider >
142185 private readonly globalStoragePath : string
@@ -268,9 +311,16 @@ export class Task extends EventEmitter<ClineEvents> {
268311 this . parentTask = parentTask
269312 this . taskNumber = taskNumber
270313
314+ // Store the task's mode when it's created
315+ // For history items, use the stored mode; for new tasks, we'll set it after getting state
271316 if ( historyItem ) {
317+ this . _taskMode = historyItem . mode || defaultModeSlug
318+ this . taskModeReady = Promise . resolve ( )
272319 TelemetryService . instance . captureTaskRestarted ( this . taskId )
273320 } else {
321+ // For new tasks, don't set the mode yet - wait for async initialization
322+ this . _taskMode = undefined
323+ this . taskModeReady = this . initializeTaskMode ( provider )
274324 TelemetryService . instance . captureTaskCreated ( this . taskId )
275325 }
276326
@@ -307,6 +357,129 @@ export class Task extends EventEmitter<ClineEvents> {
307357 }
308358 }
309359
360+ /**
361+ * Initialize the task mode from the provider state.
362+ * This method handles async initialization with proper error handling.
363+ *
364+ * ## Flow
365+ * 1. Attempts to fetch the current mode from provider state
366+ * 2. Sets `_taskMode` to the fetched mode or `defaultModeSlug` if unavailable
367+ * 3. Handles errors gracefully by falling back to default mode
368+ * 4. Logs any initialization errors for debugging
369+ *
370+ * ## Error handling
371+ * - Network failures when fetching provider state
372+ * - Provider not yet initialized
373+ * - Invalid state structure
374+ *
375+ * All errors result in fallback to `defaultModeSlug` to ensure task can proceed.
376+ *
377+ * @private
378+ * @param provider - The ClineProvider instance to fetch state from
379+ * @returns Promise that resolves when initialization is complete
380+ */
381+ private async initializeTaskMode ( provider : ClineProvider ) : Promise < void > {
382+ try {
383+ const state = await provider . getState ( )
384+ this . _taskMode = state ?. mode || defaultModeSlug
385+ } catch ( error ) {
386+ // If there's an error getting state, use the default mode
387+ this . _taskMode = defaultModeSlug
388+ // Use the provider's log method for better error visibility
389+ const errorMessage = `Failed to initialize task mode: ${ error instanceof Error ? error . message : String ( error ) } `
390+ provider . log ( errorMessage )
391+ }
392+ }
393+
394+ /**
395+ * Wait for the task mode to be initialized before proceeding.
396+ * This method ensures that any operations depending on the task mode
397+ * will have access to the correct mode value.
398+ *
399+ * ## When to use
400+ * - Before accessing mode-specific configurations
401+ * - When switching between tasks with different modes
402+ * - Before operations that depend on mode-based permissions
403+ *
404+ * ## Example usage
405+ * ```typescript
406+ * // Wait for mode initialization before mode-dependent operations
407+ * await task.waitForModeInitialization();
408+ * const mode = task.taskMode; // Now safe to access synchronously
409+ *
410+ * // Or use with getTaskMode() for a one-liner
411+ * const mode = await task.getTaskMode(); // Internally waits for initialization
412+ * ```
413+ *
414+ * @returns Promise that resolves when the task mode is initialized
415+ * @public
416+ */
417+ public async waitForModeInitialization ( ) : Promise < void > {
418+ return this . taskModeReady
419+ }
420+
421+ /**
422+ * Get the task mode asynchronously, ensuring it's properly initialized.
423+ * This is the recommended way to access the task mode as it guarantees
424+ * the mode is available before returning.
425+ *
426+ * ## Async behavior
427+ * - Internally waits for `taskModeReady` promise to resolve
428+ * - Returns the initialized mode or `defaultModeSlug` as fallback
429+ * - Safe to call multiple times - subsequent calls return immediately if already initialized
430+ *
431+ * ## Example usage
432+ * ```typescript
433+ * // Safe async access
434+ * const mode = await task.getTaskMode();
435+ * console.log(`Task is running in ${mode} mode`);
436+ *
437+ * // Use in conditional logic
438+ * if (await task.getTaskMode() === 'architect') {
439+ * // Perform architect-specific operations
440+ * }
441+ * ```
442+ *
443+ * @returns Promise resolving to the task mode string
444+ * @public
445+ */
446+ public async getTaskMode ( ) : Promise < string > {
447+ await this . taskModeReady
448+ return this . _taskMode || defaultModeSlug
449+ }
450+
451+ /**
452+ * Get the task mode synchronously. This should only be used when you're certain
453+ * that the mode has already been initialized (e.g., after waitForModeInitialization).
454+ *
455+ * ## When to use
456+ * - In synchronous contexts where async/await is not available
457+ * - After explicitly waiting for initialization via `waitForModeInitialization()`
458+ * - In event handlers or callbacks where mode is guaranteed to be initialized
459+ *
460+ * ## Example usage
461+ * ```typescript
462+ * // After ensuring initialization
463+ * await task.waitForModeInitialization();
464+ * const mode = task.taskMode; // Safe synchronous access
465+ *
466+ * // In an event handler after task is started
467+ * task.on('taskStarted', () => {
468+ * console.log(`Task started in ${task.taskMode} mode`); // Safe here
469+ * });
470+ * ```
471+ *
472+ * @throws {Error } If the mode hasn't been initialized yet
473+ * @returns The task mode string
474+ * @public
475+ */
476+ public get taskMode ( ) : string {
477+ if ( this . _taskMode === undefined ) {
478+ throw new Error ( "Task mode accessed before initialization. Use getTaskMode() or wait for taskModeReady." )
479+ }
480+ return this . _taskMode
481+ }
482+
310483 static create ( options : TaskOptions ) : [ Task , Promise < void > ] {
311484 const instance = new Task ( { ...options , startTask : false } )
312485 const { images, task, historyItem } = options
@@ -411,6 +584,7 @@ export class Task extends EventEmitter<ClineEvents> {
411584 taskNumber : this . taskNumber ,
412585 globalStoragePath : this . globalStoragePath ,
413586 workspace : this . cwd ,
587+ mode : this . _taskMode || defaultModeSlug , // Use the task's own mode, not the current provider mode
414588 } )
415589
416590 this . emit ( "taskTokenUsageUpdated" , this . taskId , tokenUsage )
0 commit comments