@@ -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 ) {
@@ -2992,7 +2995,7 @@ export class ClineProvider
29922995
29932996 if ( task . parentTaskId ) {
29942997 try {
2995- await runDelegationTransition ( this as unknown as DelegationLockHost , task . parentTaskId , async ( ) => {
2998+ await ClineProvider . prototype . runDelegationTransition . call ( this , task . parentTaskId , async ( ) => {
29962999 const { historyItem : parentHistory } = await this . getTaskWithId ( task . parentTaskId ! )
29973000
29983001 if ( parentHistory ?. status === "delegated" && parentHistory ?. awaitingChildId === task . taskId ) {
@@ -3011,12 +3014,26 @@ export class ClineProvider
30113014 }
30123015 } )
30133016 } catch ( error ) {
3014- // Fail closed: if we cannot prove the parent was detached, keep the
3015- // rehydrated child disconnected from runtime parent links and prevent
3016- // this in-process child completion from reopening the parent later .
3017+ // Fail closed: if we cannot prove the parent was detached, make the
3018+ // rehydrated child standalone so later completions cannot reopen a
3019+ // stale delegated parent, even after a provider reload .
30173020 parentTask = undefined
30183021 rootTask = undefined
30193022 this . cancelledDelegationChildIds . add ( task . taskId )
3023+ historyItem = {
3024+ ...historyItem ,
3025+ parentTaskId : undefined ,
3026+ rootTaskId : undefined ,
3027+ }
3028+ try {
3029+ await this . updateTaskHistory ( historyItem )
3030+ } catch ( historyError ) {
3031+ this . log (
3032+ `[cancelTask] Failed to persist standalone child state for ${ task . taskId } : ${
3033+ historyError instanceof Error ? historyError . message : String ( historyError )
3034+ } `,
3035+ )
3036+ }
30203037 this . log (
30213038 `[cancelTask] Failed to detach delegated parent for ${ task . taskId } : ${
30223039 error instanceof Error ? error . message : String ( error )
@@ -3379,7 +3396,7 @@ export class ClineProvider
33793396 completionResultSummary : string
33803397 } ) : Promise < boolean > {
33813398 const { parentTaskId, childTaskId, completionResultSummary } = params
3382- return await runDelegationTransition ( this as unknown as DelegationLockHost , parentTaskId , async ( ) => {
3399+ return await ( ClineProvider . prototype . runDelegationTransition . call ( this , parentTaskId , async ( ) => {
33833400 const globalStoragePath = this . contextProxy . globalStorageUri . fsPath
33843401
33853402 // 1) Load parent from history and current persisted messages
@@ -3588,7 +3605,7 @@ export class ClineProvider
35883605
35893606 ; ( this . cancelledDelegationChildIds as Set < string > | undefined ) ?. delete ( childTaskId )
35903607 return true
3591- } )
3608+ } ) as Promise < boolean > )
35923609 }
35933610
35943611 /**
0 commit comments