Skip to content

Commit b51af98

Browse files
authored
fix: make delegation reopen flow Roo v2-native (#11418)
1 parent b759b92 commit b51af98

4 files changed

Lines changed: 226 additions & 73 deletions

File tree

src/__tests__/history-resume-delegation.spec.ts

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,19 @@ vi.mock("vscode", () => {
2929
vi.mock("../core/task-persistence/taskMessages", () => ({
3030
readTaskMessages: vi.fn().mockResolvedValue([]),
3131
}))
32-
vi.mock("../core/task-persistence", () => ({
33-
readApiMessages: vi.fn().mockResolvedValue([]),
34-
saveApiMessages: vi.fn().mockResolvedValue(undefined),
35-
saveTaskMessages: vi.fn().mockResolvedValue(undefined),
36-
}))
32+
vi.mock("../core/task-persistence", async (importOriginal) => {
33+
const actual = await importOriginal<typeof import("../core/task-persistence")>()
34+
return {
35+
...actual,
36+
readRooMessages: vi.fn().mockResolvedValue([]),
37+
saveRooMessages: vi.fn().mockResolvedValue(undefined),
38+
saveTaskMessages: vi.fn().mockResolvedValue(undefined),
39+
}
40+
})
3741

3842
import { ClineProvider } from "../core/webview/ClineProvider"
3943
import { readTaskMessages } from "../core/task-persistence/taskMessages"
40-
import { readApiMessages, saveApiMessages, saveTaskMessages } from "../core/task-persistence"
44+
import { readRooMessages, saveRooMessages, saveTaskMessages } from "../core/task-persistence"
4145

4246
describe("History resume delegation - parent metadata transitions", () => {
4347
beforeEach(() => {
@@ -83,7 +87,7 @@ describe("History resume delegation - parent metadata transitions", () => {
8387

8488
// Mock persistence reads to return empty arrays
8589
vi.mocked(readTaskMessages).mockResolvedValue([])
86-
vi.mocked(readApiMessages).mockResolvedValue([])
90+
vi.mocked(readRooMessages).mockResolvedValue([])
8791

8892
await (ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
8993
parentTaskId: "parent-1",
@@ -152,7 +156,7 @@ describe("History resume delegation - parent metadata transitions", () => {
152156
const existingApiMessages = [{ role: "user", content: [{ type: "text", text: "Old request" }], ts: 50 }]
153157

154158
vi.mocked(readTaskMessages).mockResolvedValue(existingUiMessages as any)
155-
vi.mocked(readApiMessages).mockResolvedValue(existingApiMessages as any)
159+
vi.mocked(readRooMessages).mockResolvedValue(existingApiMessages as any)
156160

157161
await (ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
158162
parentTaskId: "p1",
@@ -176,7 +180,7 @@ describe("History resume delegation - parent metadata transitions", () => {
176180
)
177181

178182
// Verify API history injection (user role message)
179-
expect(saveApiMessages).toHaveBeenCalledWith(
183+
expect(saveRooMessages).toHaveBeenCalledWith(
180184
expect.objectContaining({
181185
messages: expect.arrayContaining([
182186
expect.objectContaining({
@@ -198,11 +202,11 @@ describe("History resume delegation - parent metadata transitions", () => {
198202
const uiCall = vi.mocked(saveTaskMessages).mock.calls[0][0]
199203
expect(uiCall.messages).toHaveLength(2) // 1 original + 1 injected
200204

201-
const apiCall = vi.mocked(saveApiMessages).mock.calls[0][0]
205+
const apiCall = vi.mocked(saveRooMessages).mock.calls[0][0]
202206
expect(apiCall.messages).toHaveLength(2) // 1 original + 1 injected
203207
})
204208

205-
it("reopenParentFromDelegation injects tool_result when new_task tool_use exists in API history", async () => {
209+
it("reopenParentFromDelegation injects tool-result message when new_task tool_use exists in API history", async () => {
206210
const provider = {
207211
contextProxy: { globalStorageUri: { fsPath: "/storage" } },
208212
getTaskWithId: vi.fn().mockResolvedValue({
@@ -249,25 +253,27 @@ describe("History resume delegation - parent metadata transitions", () => {
249253
]
250254

251255
vi.mocked(readTaskMessages).mockResolvedValue(existingUiMessages as any)
252-
vi.mocked(readApiMessages).mockResolvedValue(existingApiMessages as any)
256+
vi.mocked(readRooMessages).mockResolvedValue(existingApiMessages as any)
253257

254258
await (ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
255259
parentTaskId: "p-tool",
256260
childTaskId: "c-tool",
257261
completionResultSummary: "Subtask completed via tool_result",
258262
})
259263

260-
// Verify API history injection uses tool_result (not text fallback)
261-
expect(saveApiMessages).toHaveBeenCalledWith(
264+
// Verify API history injection uses tool-result (not text fallback)
265+
expect(saveRooMessages).toHaveBeenCalledWith(
262266
expect.objectContaining({
263267
messages: expect.arrayContaining([
264268
expect.objectContaining({
265-
role: "user",
269+
role: "tool",
266270
content: expect.arrayContaining([
267271
expect.objectContaining({
268-
type: "tool_result",
269-
tool_use_id: "toolu_abc123",
270-
content: expect.stringContaining("Subtask c-tool completed"),
272+
type: "tool-result",
273+
toolCallId: "toolu_abc123",
274+
output: expect.objectContaining({
275+
value: expect.stringContaining("Subtask c-tool completed"),
276+
}),
271277
}),
272278
]),
273279
}),
@@ -277,15 +283,15 @@ describe("History resume delegation - parent metadata transitions", () => {
277283
}),
278284
)
279285

280-
// Verify total message count: 2 original + 1 injected user message with tool_result
281-
const apiCall = vi.mocked(saveApiMessages).mock.calls[0][0]
286+
// Verify total message count: 2 original + 1 injected tool message with tool-result
287+
const apiCall = vi.mocked(saveRooMessages).mock.calls[0][0]
282288
expect(apiCall.messages).toHaveLength(3)
283289

284-
// Verify the injected message is a user message with tool_result type
285-
const injectedMsg = apiCall.messages[2]
286-
expect(injectedMsg.role).toBe("user")
287-
expect((injectedMsg.content[0] as any).type).toBe("tool_result")
288-
expect((injectedMsg.content[0] as any).tool_use_id).toBe("toolu_abc123")
290+
// Verify the injected message is a tool message with tool-result type
291+
const injectedMsg = apiCall.messages[2] as any
292+
expect(injectedMsg.role).toBe("tool")
293+
expect((injectedMsg.content[0] as any).type).toBe("tool-result")
294+
expect((injectedMsg.content[0] as any).toolCallId).toBe("toolu_abc123")
289295
})
290296

291297
it("reopenParentFromDelegation injects plain text when no new_task tool_use exists in API history", async () => {
@@ -321,18 +327,18 @@ describe("History resume delegation - parent metadata transitions", () => {
321327
const existingApiMessages = [{ role: "user", content: [{ type: "text", text: "Create a subtask" }], ts: 40 }]
322328

323329
vi.mocked(readTaskMessages).mockResolvedValue(existingUiMessages as any)
324-
vi.mocked(readApiMessages).mockResolvedValue(existingApiMessages as any)
330+
vi.mocked(readRooMessages).mockResolvedValue(existingApiMessages as any)
325331

326332
await (ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
327333
parentTaskId: "p-no-tool",
328334
childTaskId: "c-no-tool",
329335
completionResultSummary: "Subtask completed without tool_use",
330336
})
331337

332-
const apiCall = vi.mocked(saveApiMessages).mock.calls[0][0]
338+
const apiCall = vi.mocked(saveRooMessages).mock.calls[0][0]
333339
// Should append a user text note
334340
expect(apiCall.messages).toHaveLength(2)
335-
const injected = apiCall.messages[1]
341+
const injected = apiCall.messages[1] as any
336342
expect(injected.role).toBe("user")
337343
expect((injected.content[0] as any).type).toBe("text")
338344
expect((injected.content[0] as any).text).toContain("Subtask c-no-tool completed")
@@ -372,7 +378,7 @@ describe("History resume delegation - parent metadata transitions", () => {
372378
} as unknown as ClineProvider
373379

374380
vi.mocked(readTaskMessages).mockResolvedValue([])
375-
vi.mocked(readApiMessages).mockResolvedValue([])
381+
vi.mocked(readRooMessages).mockResolvedValue([])
376382

377383
await (ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
378384
parentTaskId: "parent-2",
@@ -416,7 +422,7 @@ describe("History resume delegation - parent metadata transitions", () => {
416422
} as unknown as ClineProvider
417423

418424
vi.mocked(readTaskMessages).mockResolvedValue([])
419-
vi.mocked(readApiMessages).mockResolvedValue([])
425+
vi.mocked(readRooMessages).mockResolvedValue([])
420426

421427
await (ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
422428
parentTaskId: "p3",
@@ -494,7 +500,7 @@ describe("History resume delegation - parent metadata transitions", () => {
494500
} as unknown as ClineProvider
495501

496502
vi.mocked(readTaskMessages).mockResolvedValue([])
497-
vi.mocked(readApiMessages).mockResolvedValue([])
503+
vi.mocked(readRooMessages).mockResolvedValue([])
498504

499505
await expect(
500506
(ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
@@ -552,7 +558,7 @@ describe("History resume delegation - parent metadata transitions", () => {
552558
} as unknown as ClineProvider
553559

554560
vi.mocked(readTaskMessages).mockResolvedValue([])
555-
vi.mocked(readApiMessages).mockResolvedValue([])
561+
vi.mocked(readRooMessages).mockResolvedValue([])
556562

557563
await (ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
558564
parentTaskId: "p4",
@@ -616,7 +622,7 @@ describe("History resume delegation - parent metadata transitions", () => {
616622
} as unknown as ClineProvider
617623

618624
vi.mocked(readTaskMessages).mockResolvedValue([])
619-
vi.mocked(readApiMessages).mockResolvedValue([])
625+
vi.mocked(readRooMessages).mockResolvedValue([])
620626

621627
await (ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
622628
parentTaskId: "parent-rpd02",
@@ -697,7 +703,7 @@ describe("History resume delegation - parent metadata transitions", () => {
697703
} as unknown as ClineProvider
698704

699705
vi.mocked(readTaskMessages).mockResolvedValue([])
700-
vi.mocked(readApiMessages).mockResolvedValue([])
706+
vi.mocked(readRooMessages).mockResolvedValue([])
701707

702708
await expect(
703709
(ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
@@ -752,7 +758,7 @@ describe("History resume delegation - parent metadata transitions", () => {
752758

753759
// Mock read failures or empty returns
754760
vi.mocked(readTaskMessages).mockResolvedValue([])
755-
vi.mocked(readApiMessages).mockResolvedValue([])
761+
vi.mocked(readRooMessages).mockResolvedValue([])
756762

757763
await expect(
758764
(ClineProvider.prototype as any).reopenParentFromDelegation.call(provider, {
@@ -774,7 +780,7 @@ describe("History resume delegation - parent metadata transitions", () => {
774780
}),
775781
)
776782

777-
expect(saveApiMessages).toHaveBeenCalledWith(
783+
expect(saveRooMessages).toHaveBeenCalledWith(
778784
expect.objectContaining({
779785
messages: [
780786
expect.objectContaining({

src/__tests__/nested-delegation-resume.spec.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,21 @@ vi.mock("vscode", () => {
4343
vi.mock("../core/task-persistence/taskMessages", () => ({
4444
readTaskMessages: vi.fn().mockResolvedValue([]),
4545
}))
46-
vi.mock("../core/task-persistence", () => ({
47-
readApiMessages: vi.fn().mockResolvedValue([]),
48-
saveApiMessages: vi.fn().mockResolvedValue(undefined),
49-
saveTaskMessages: vi.fn().mockResolvedValue(undefined),
50-
}))
46+
vi.mock("../core/task-persistence", async (importOriginal) => {
47+
const actual = await importOriginal<typeof import("../core/task-persistence")>()
48+
return {
49+
...actual,
50+
readRooMessages: vi.fn().mockResolvedValue([]),
51+
saveRooMessages: vi.fn().mockResolvedValue(undefined),
52+
saveTaskMessages: vi.fn().mockResolvedValue(undefined),
53+
}
54+
})
5155

5256
import { attemptCompletionTool } from "../core/tools/AttemptCompletionTool"
5357
import { ClineProvider } from "../core/webview/ClineProvider"
5458
import type { Task } from "../core/task/Task"
5559
import { readTaskMessages } from "../core/task-persistence/taskMessages"
56-
import { readApiMessages, saveApiMessages, saveTaskMessages } from "../core/task-persistence"
60+
import { readRooMessages, saveRooMessages, saveTaskMessages } from "../core/task-persistence"
5761

5862
describe("Nested delegation resume (A → B → C)", () => {
5963
beforeEach(() => {
@@ -164,7 +168,7 @@ describe("Nested delegation resume (A → B → C)", () => {
164168

165169
// Empty histories for simplicity
166170
vi.mocked(readTaskMessages).mockResolvedValue([])
167-
vi.mocked(readApiMessages).mockResolvedValue([])
171+
vi.mocked(readRooMessages).mockResolvedValue([])
168172

169173
// Step 1: C completes -> should reopen B automatically
170174
const clineC = {
@@ -174,6 +178,7 @@ describe("Nested delegation resume (A → B → C)", () => {
174178
historyItem: { parentTaskId: "B" },
175179
providerRef: { deref: () => provider },
176180
say: vi.fn().mockResolvedValue(undefined),
181+
ask: vi.fn().mockResolvedValue({ response: "yesButtonClicked", text: undefined, images: undefined }),
177182
emit: vi.fn(),
178183
getTokenUsage: vi.fn(() => ({})),
179184
toolUsage: {},
@@ -221,6 +226,7 @@ describe("Nested delegation resume (A → B → C)", () => {
221226
historyItem: { parentTaskId: "A" },
222227
providerRef: { deref: () => provider },
223228
say: vi.fn().mockResolvedValue(undefined),
229+
ask: vi.fn().mockResolvedValue({ response: "yesButtonClicked", text: undefined, images: undefined }),
224230
emit: vi.fn(),
225231
getTokenUsage: vi.fn(() => ({})),
226232
toolUsage: {},

0 commit comments

Comments
 (0)