Skip to content

Commit 1bc2eef

Browse files
committed
feat: persist parent-child task relationships across extension reloads
- Add parentTaskId, rootTaskId, and taskHierarchy fields to HistoryItem schema - Store task IDs alongside object references in Task class - Update taskMetadata to save parent-child relationship data - Modify ClineProvider to restore task hierarchy when loading from history - Implement getTaskHierarchy() method to build hierarchy array Fixes #6624
1 parent 82a007a commit 1bc2eef

File tree

4 files changed

+67
-2
lines changed

4 files changed

+67
-2
lines changed

packages/types/src/history.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ export const historyItemSchema = z.object({
1717
size: z.number().optional(),
1818
workspace: z.string().optional(),
1919
mode: z.string().optional(),
20+
parentTaskId: z.string().optional(),
21+
rootTaskId: z.string().optional(),
22+
taskHierarchy: z.array(z.string()).optional(),
2023
})
2124

2225
export type HistoryItem = z.infer<typeof historyItemSchema>

src/core/task-persistence/taskMetadata.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export type TaskMetadataOptions = {
1919
globalStoragePath: string
2020
workspace: string
2121
mode?: string
22+
parentTaskId?: string
23+
rootTaskId?: string
24+
taskHierarchy?: string[]
2225
}
2326

2427
export async function taskMetadata({
@@ -28,6 +31,9 @@ export async function taskMetadata({
2831
globalStoragePath,
2932
workspace,
3033
mode,
34+
parentTaskId,
35+
rootTaskId,
36+
taskHierarchy,
3137
}: TaskMetadataOptions) {
3238
const taskDir = await getTaskDirectoryPath(globalStoragePath, taskId)
3339

@@ -95,6 +101,9 @@ export async function taskMetadata({
95101
size: taskDirSize,
96102
workspace,
97103
mode,
104+
parentTaskId,
105+
rootTaskId,
106+
taskHierarchy,
98107
}
99108

100109
return { historyItem, tokenUsage }

src/core/task/Task.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
129129
readonly taskNumber: number
130130
readonly workspacePath: string
131131

132+
// Store task IDs for persistence
133+
readonly rootTaskId: string | undefined = undefined
134+
readonly parentTaskId: string | undefined = undefined
135+
132136
/**
133137
* The mode associated with this task. Persisted across sessions
134138
* to maintain user context when reopening tasks from history.
@@ -307,6 +311,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
307311
this.parentTask = parentTask
308312
this.taskNumber = taskNumber
309313

314+
// Store task IDs for persistence
315+
this.rootTaskId = rootTask?.taskId
316+
this.parentTaskId = parentTask?.taskId
317+
310318
// Store the task's mode when it's created.
311319
// For history items, use the stored mode; for new tasks, we'll set it
312320
// after getting state.
@@ -582,6 +590,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
582590
globalStoragePath: this.globalStoragePath,
583591
workspace: this.cwd,
584592
mode: this._taskMode || defaultModeSlug, // Use the task's own mode, not the current provider mode
593+
parentTaskId: this.parentTaskId,
594+
rootTaskId: this.rootTaskId,
595+
taskHierarchy: this.getTaskHierarchy(),
585596
})
586597

587598
this.emit(RooCodeEventName.TaskTokenUsageUpdated, this.taskId, tokenUsage)
@@ -2152,4 +2163,17 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
21522163
public get cwd() {
21532164
return this.workspacePath
21542165
}
2166+
2167+
// Get task hierarchy for persistence
2168+
public getTaskHierarchy(): string[] {
2169+
const hierarchy: string[] = []
2170+
let currentTask: Task | undefined = this.parentTask
2171+
2172+
while (currentTask) {
2173+
hierarchy.unshift(currentTask.taskId)
2174+
currentTask = currentTask.parentTask
2175+
}
2176+
2177+
return hierarchy
2178+
}
21552179
}

src/core/webview/ClineProvider.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,18 @@ export class ClineProvider
740740
experiments,
741741
} = await this.getState()
742742

743+
// Restore parent and root tasks if their IDs are stored in the history item
744+
let rootTask: Task | undefined = historyItem.rootTask
745+
let parentTask: Task | undefined = historyItem.parentTask
746+
747+
// If we don't have the actual task objects but have their IDs, try to find them in the stack
748+
if (!rootTask && historyItem.rootTaskId) {
749+
rootTask = this.clineStack.find((task) => task.taskId === historyItem.rootTaskId)
750+
}
751+
if (!parentTask && historyItem.parentTaskId) {
752+
parentTask = this.clineStack.find((task) => task.taskId === historyItem.parentTaskId)
753+
}
754+
743755
const task = new Task({
744756
provider: this,
745757
apiConfiguration,
@@ -749,8 +761,8 @@ export class ClineProvider
749761
consecutiveMistakeLimit: apiConfiguration.consecutiveMistakeLimit,
750762
historyItem,
751763
experiments,
752-
rootTask: historyItem.rootTask,
753-
parentTask: historyItem.parentTask,
764+
rootTask,
765+
parentTask,
754766
taskNumber: historyItem.number,
755767
onCreated: (instance) => this.emit(RooCodeEventName.TaskCreated, instance),
756768
})
@@ -1344,6 +1356,23 @@ export class ClineProvider
13441356
if (id !== this.getCurrentCline()?.taskId) {
13451357
// Non-current task.
13461358
const { historyItem } = await this.getTaskWithId(id)
1359+
1360+
// Check if this task has parent/child relationships that need to be restored
1361+
if (historyItem.taskHierarchy && historyItem.taskHierarchy.length > 0) {
1362+
// Restore the entire task hierarchy from root to this task
1363+
const taskHistory = this.getGlobalState("taskHistory") ?? []
1364+
1365+
// First, restore all parent tasks in the hierarchy
1366+
for (const taskId of historyItem.taskHierarchy) {
1367+
const parentHistoryItem = taskHistory.find((item: HistoryItem) => item.id === taskId)
1368+
if (parentHistoryItem && !this.clineStack.find((task) => task.taskId === taskId)) {
1369+
// This parent task is not in the stack, so restore it
1370+
await this.initClineWithHistoryItem(parentHistoryItem)
1371+
}
1372+
}
1373+
}
1374+
1375+
// Now restore the requested task
13471376
await this.initClineWithHistoryItem(historyItem) // Clears existing task.
13481377
}
13491378

0 commit comments

Comments
 (0)