@@ -114,13 +114,11 @@ export type ClineProviderEvents = {
114114 clineCreated : [ cline : Task ]
115115}
116116
117- type DelegationLockHost = {
118- delegationTransitionLocks ?: Map < string , Promise < void > >
119- }
120-
121- function runDelegationTransition < T > ( host : DelegationLockHost , parentTaskId : string , fn : ( ) => Promise < T > ) : Promise < T > {
122- host . delegationTransitionLocks ??= new Map ( )
123- const locks = host . delegationTransitionLocks
117+ function runDelegationTransition < T > (
118+ locks : Map < string , Promise < void > > ,
119+ parentTaskId : string ,
120+ fn : ( ) => Promise < T > ,
121+ ) : Promise < T > {
124122 const previous = locks . get ( parentTaskId ) ?? Promise . resolve ( )
125123 const current = previous . then ( fn , fn )
126124 const tail = current . then (
@@ -153,7 +151,7 @@ export class ClineProvider
153151 private webviewDisposables : vscode . Disposable [ ] = [ ]
154152 private view ?: vscode . WebviewView | vscode . WebviewPanel
155153 private clineStack : Task [ ] = [ ]
156- private delegationTransitionLocks = new Map < string , Promise < void > > ( )
154+ private delegationTransitionLocks ?: Map < string , Promise < void > >
157155 private cancelledDelegationChildIds = new Set < string > ( )
158156 private codeIndexStatusSubscription ?: vscode . Disposable
159157 private codeIndexManager ?: CodeIndexManager
@@ -173,6 +171,11 @@ export class ClineProvider
173171 private globalStateWriteThroughTimer : ReturnType < typeof setTimeout > | null = null
174172 private static readonly GLOBAL_STATE_WRITE_THROUGH_DEBOUNCE_MS = 5000 // 5 seconds
175173 private static readonly PENDING_OPERATION_TIMEOUT_MS = 30000 // 30 seconds
174+
175+ private runDelegationTransition < T > ( parentTaskId : string , fn : ( ) => Promise < T > ) : Promise < T > {
176+ this . delegationTransitionLocks ??= new Map ( )
177+ return runDelegationTransition ( this . delegationTransitionLocks , parentTaskId , fn )
178+ }
176179 private readonly pendingEditOperations : PendingEditOperationStore
177180
178181 private cloudOrganizationsCache : CloudOrganizationMembership [ ] | null = null
@@ -509,7 +512,7 @@ export class ClineProvider
509512 // child and will update the parent to point at the new child.
510513 if ( parentTaskId && childTaskId && ! options ?. skipDelegationRepair ) {
511514 try {
512- await runDelegationTransition ( this as unknown as DelegationLockHost , parentTaskId , async ( ) => {
515+ await ClineProvider . prototype . runDelegationTransition . call ( this , parentTaskId , async ( ) => {
513516 const { historyItem : parentHistory } = await this . getTaskWithId ( parentTaskId )
514517
515518 if ( parentHistory ?. status === "delegated" && parentHistory ?. awaitingChildId === childTaskId ) {
@@ -2995,7 +2998,7 @@ export class ClineProvider
29952998
29962999 if ( task . parentTaskId ) {
29973000 try {
2998- await runDelegationTransition ( this as unknown as DelegationLockHost , task . parentTaskId , async ( ) => {
3001+ await ClineProvider . prototype . runDelegationTransition . call ( this , task . parentTaskId , async ( ) => {
29993002 const { historyItem : parentHistory } = await this . getTaskWithId ( task . parentTaskId ! )
30003003
30013004 if ( parentHistory ?. status === "delegated" && parentHistory ?. awaitingChildId === task . taskId ) {
@@ -3014,12 +3017,26 @@ export class ClineProvider
30143017 }
30153018 } )
30163019 } catch ( error ) {
3017- // Fail closed: if we cannot prove the parent was detached, keep the
3018- // rehydrated child disconnected from runtime parent links and prevent
3019- // this in-process child completion from reopening the parent later .
3020+ // Fail closed: if we cannot prove the parent was detached, make the
3021+ // rehydrated child standalone so later completions cannot reopen a
3022+ // stale delegated parent, even after a provider reload .
30203023 parentTask = undefined
30213024 rootTask = undefined
30223025 this . cancelledDelegationChildIds . add ( task . taskId )
3026+ historyItem = {
3027+ ...historyItem ,
3028+ parentTaskId : undefined ,
3029+ rootTaskId : undefined ,
3030+ }
3031+ try {
3032+ await this . updateTaskHistory ( historyItem )
3033+ } catch ( historyError ) {
3034+ this . log (
3035+ `[cancelTask] Failed to persist standalone child state for ${ task . taskId } : ${
3036+ historyError instanceof Error ? historyError . message : String ( historyError )
3037+ } `,
3038+ )
3039+ }
30233040 this . log (
30243041 `[cancelTask] Failed to detach delegated parent for ${ task . taskId } : ${
30253042 error instanceof Error ? error . message : String ( error )
@@ -3382,7 +3399,7 @@ export class ClineProvider
33823399 completionResultSummary : string
33833400 } ) : Promise < boolean > {
33843401 const { parentTaskId, childTaskId, completionResultSummary } = params
3385- return await runDelegationTransition ( this as unknown as DelegationLockHost , parentTaskId , async ( ) => {
3402+ return await ( ClineProvider . prototype . runDelegationTransition . call ( this , parentTaskId , async ( ) => {
33863403 const globalStoragePath = this . contextProxy . globalStorageUri . fsPath
33873404
33883405 // 1) Load parent from history and current persisted messages
@@ -3591,7 +3608,7 @@ export class ClineProvider
35913608
35923609 ; ( this . cancelledDelegationChildIds as Set < string > | undefined ) ?. delete ( childTaskId )
35933610 return true
3594- } )
3611+ } ) as Promise < boolean > )
35953612 }
35963613
35973614 /**
0 commit comments