@@ -456,7 +456,7 @@ export class ClineProvider
456456 await this . removeClineFromStack ( )
457457 // Resume the last cline instance in the stack (if it exists - this is
458458 // the 'parent' calling task).
459- await this . getCurrentTask ( ) ?. completeSubtask ( lastMessage )
459+ await this . continueParentTask ( lastMessage )
460460 }
461461 // Pending Edit Operations Management
462462
@@ -1482,12 +1482,209 @@ export class ClineProvider
14821482 if ( id !== this . getCurrentTask ( ) ?. taskId ) {
14831483 // Non-current task.
14841484 const { historyItem } = await this . getTaskWithId ( id )
1485- await this . createTaskWithHistoryItem ( historyItem ) // Clears existing task.
1485+ // If this is a subtask, we need to reconstruct the entire task stack
1486+ if ( historyItem . parentTaskId || historyItem . rootTaskId ) {
1487+ await this . reconstructTaskStack ( historyItem )
1488+ } else {
1489+ // For standalone tasks, use the normal flow
1490+ await this . createTaskWithHistoryItem ( historyItem )
1491+ }
14861492 }
14871493
14881494 await this . postMessageToWebview ( { type : "action" , action : "chatButtonClicked" } )
14891495 }
14901496
1497+ private async continueParentTask ( lastMessage : string ) : Promise < void > {
1498+ const parentTask = this . getCurrentTask ( )
1499+ if ( parentTask ) {
1500+ this . log ( `[continueParentTask] Found parent task ${ parentTask . taskId } , isPaused: ${ parentTask . isPaused } ` )
1501+ this . log ( `[continueParentTask] Parent task isInitialized: ${ parentTask . isInitialized } ` )
1502+
1503+ try {
1504+ // If the parent task is not initialized, we need to initialize it properly
1505+ if ( ! parentTask . isInitialized ) {
1506+ this . log ( `[continueParentTask] Initializing parent task from history` )
1507+ // Load the parent task's saved messages and API conversation
1508+ parentTask . clineMessages = await parentTask . getSavedClineMessages ( )
1509+ parentTask . apiConversationHistory = await parentTask . getSavedApiConversationHistory ( )
1510+ parentTask . isInitialized = true
1511+ this . log (
1512+ `[continueParentTask] Parent task initialized with ${ parentTask . clineMessages . length } messages` ,
1513+ )
1514+ }
1515+
1516+ // Complete the subtask on the existing parent task
1517+ // This will add the subtask result to the parent's conversation and unpause it
1518+ await parentTask . completeSubtask ( lastMessage )
1519+ this . log ( `[continueParentTask] Parent task ${ parentTask . taskId } subtask completed` )
1520+
1521+ // Check if the parent task needs to continue its execution
1522+ // If the parent task was created from history reconstruction, it may not have
1523+ // an active execution loop running, so we need to continue it manually
1524+ if ( ! parentTask . isPaused && parentTask . isInitialized ) {
1525+ this . log ( `[continueParentTask] Parent task is unpaused and initialized, continuing execution` )
1526+
1527+ // Continue the parent task's execution with the subtask result
1528+ // The subtask result has already been added to the conversation by completeSubtask
1529+ // Now we need to continue the execution loop
1530+ const continueExecution = async ( ) => {
1531+ try {
1532+ // Continue the task loop with an empty user content since the subtask result
1533+ // has already been added to the API conversation history
1534+ await parentTask . recursivelyMakeClineRequests ( [ ] , false )
1535+ } catch ( error ) {
1536+ this . log (
1537+ `[continueParentTask] Error continuing parent task execution: ${ error instanceof Error ? error . message : String ( error ) } ` ,
1538+ )
1539+ }
1540+ }
1541+ // Start the continuation in the background to avoid blocking
1542+ continueExecution ( )
1543+ }
1544+
1545+ // Update the webview to show the parent task
1546+ this . log ( `[continueParentTask] Updating webview state` )
1547+ await this . postStateToWebview ( )
1548+ this . log ( `[continueParentTask] Webview state updated` )
1549+ } catch ( error ) {
1550+ this . log (
1551+ `[continueParentTask] Error during parent task resumption: ${ error instanceof Error ? error . message : String ( error ) } ` ,
1552+ )
1553+ throw error
1554+ }
1555+ } else {
1556+ this . log ( `[continueParentTask] No parent task found in stack` )
1557+ }
1558+ }
1559+
1560+ /**
1561+ * Reconstructs the entire task stack for a subtask by loading and adding
1562+ * all parent tasks to the stack in the correct order, then adding the target subtask.
1563+ * This ensures that when the subtask finishes, control returns to the parent task.
1564+ */
1565+ private async reconstructTaskStack ( targetHistoryItem : HistoryItem ) : Promise < void > {
1566+ // Clear the current stack
1567+ await this . removeClineFromStack ( )
1568+
1569+ // Build the task hierarchy from root to target
1570+ const taskHierarchy = await this . buildTaskHierarchy ( targetHistoryItem )
1571+
1572+ this . log ( `[reconstructTaskStack] Reconstructing stack with ${ taskHierarchy . length } tasks` )
1573+
1574+ const createdTasks : Task [ ] = [ ]
1575+
1576+ // Create all tasks in the hierarchy with proper parent/root references
1577+ for ( let i = 0 ; i < taskHierarchy . length ; i ++ ) {
1578+ const historyItem = taskHierarchy [ i ]
1579+ const isTargetTask = i === taskHierarchy . length - 1
1580+
1581+ // Determine parent and root task references
1582+ const parentTask = i > 0 ? createdTasks [ i - 1 ] : undefined
1583+ const rootTask = createdTasks [ 0 ] || undefined
1584+
1585+ // Create the task with proper parent/root references
1586+ const task = await this . createTaskFromHistoryItem ( historyItem , isTargetTask , parentTask , rootTask )
1587+
1588+ // Pause parent tasks so only the target runs
1589+ if ( ! isTargetTask ) {
1590+ task . isPaused = true
1591+ this . log ( `[reconstructTaskStack] Added paused parent task ${ task . taskId } ` )
1592+ } else {
1593+ this . log ( `[reconstructTaskStack] Added and started target task ${ task . taskId } ` )
1594+ }
1595+
1596+ createdTasks . push ( task )
1597+ await this . addClineToStack ( task )
1598+ }
1599+
1600+ // Establish parent-child relationships after all tasks are created
1601+ for ( let i = 0 ; i < createdTasks . length - 1 ; i ++ ) {
1602+ const parentTask = createdTasks [ i ]
1603+ const childTask = createdTasks [ i + 1 ]
1604+
1605+ // Set the childTaskId on the parent to point to the child
1606+ parentTask . childTaskId = childTask . taskId
1607+ this . log ( `[reconstructTaskStack] Linked parent ${ parentTask . taskId } to child ${ childTask . taskId } ` )
1608+ }
1609+ }
1610+
1611+ /**
1612+ * Builds the complete task hierarchy from root to target task.
1613+ * Returns an array of HistoryItems in execution order (root first, target last).
1614+ */
1615+ private async buildTaskHierarchy ( targetHistoryItem : HistoryItem ) : Promise < HistoryItem [ ] > {
1616+ const hierarchy : HistoryItem [ ] = [ ]
1617+ const visited = new Set < string > ( )
1618+
1619+ // Recursive function to build hierarchy
1620+ const addToHierarchy = async ( historyItem : HistoryItem ) : Promise < void > => {
1621+ // Prevent infinite loops
1622+ if ( visited . has ( historyItem . id ) ) {
1623+ return
1624+ }
1625+ visited . add ( historyItem . id )
1626+
1627+ // If this task has a parent, add the parent first
1628+ if ( historyItem . parentTaskId ) {
1629+ try {
1630+ const { historyItem : parentHistoryItem } = await this . getTaskWithId ( historyItem . parentTaskId )
1631+ await addToHierarchy ( parentHistoryItem )
1632+ } catch ( error ) {
1633+ this . log (
1634+ `[buildTaskHierarchy] Failed to load parent task ${ historyItem . parentTaskId } : ${ error instanceof Error ? error . message : String ( error ) } ` ,
1635+ )
1636+ }
1637+ }
1638+
1639+ // Add this task to the hierarchy
1640+ hierarchy . push ( historyItem )
1641+ }
1642+
1643+ await addToHierarchy ( targetHistoryItem )
1644+ return hierarchy
1645+ }
1646+
1647+ /**
1648+ * Creates a Task instance from a HistoryItem.
1649+ * Used for reconstructing the task stack.
1650+ */
1651+ private async createTaskFromHistoryItem (
1652+ historyItem : HistoryItem ,
1653+ shouldStart : boolean = false ,
1654+ parentTask ?: Task ,
1655+ rootTask ?: Task ,
1656+ ) : Promise < Task > {
1657+ const {
1658+ apiConfiguration,
1659+ diffEnabled : enableDiff ,
1660+ enableCheckpoints,
1661+ fuzzyMatchThreshold,
1662+ experiments,
1663+ cloudUserInfo,
1664+ remoteControlEnabled,
1665+ } = await this . getState ( )
1666+
1667+ const task = new Task ( {
1668+ provider : this ,
1669+ apiConfiguration,
1670+ enableDiff,
1671+ enableCheckpoints,
1672+ fuzzyMatchThreshold,
1673+ consecutiveMistakeLimit : apiConfiguration . consecutiveMistakeLimit ,
1674+ historyItem,
1675+ experiments,
1676+ parentTask, // Pass the actual parent Task object
1677+ rootTask, // Pass the actual root Task object
1678+ taskNumber : historyItem . number ,
1679+ workspacePath : historyItem . workspace ,
1680+ onCreated : this . taskCreationCallback ,
1681+ enableBridge : BridgeOrchestrator . isEnabled ( cloudUserInfo , remoteControlEnabled ) ,
1682+ startTask : shouldStart , // Only start the target task
1683+ } )
1684+
1685+ return task
1686+ }
1687+
14911688 async exportTaskWithId ( id : string ) {
14921689 const { historyItem, apiConversationHistory } = await this . getTaskWithId ( id )
14931690 await downloadTask ( historyItem . ts , apiConversationHistory )
0 commit comments