Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 42 additions & 36 deletions src/__tests__/history-resume-delegation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,19 @@ vi.mock("vscode", () => {
vi.mock("../core/task-persistence/taskMessages", () => ({
readTaskMessages: vi.fn().mockResolvedValue([]),
}))
vi.mock("../core/task-persistence", () => ({
readApiMessages: vi.fn().mockResolvedValue([]),
saveApiMessages: vi.fn().mockResolvedValue(undefined),
saveTaskMessages: vi.fn().mockResolvedValue(undefined),
}))
vi.mock("../core/task-persistence", async (importOriginal) => {
const actual = await importOriginal<typeof import("../core/task-persistence")>()
return {
...actual,
readRooMessages: vi.fn().mockResolvedValue([]),
saveRooMessages: vi.fn().mockResolvedValue(undefined),
saveTaskMessages: vi.fn().mockResolvedValue(undefined),
}
})

import { ClineProvider } from "../core/webview/ClineProvider"
import { readTaskMessages } from "../core/task-persistence/taskMessages"
import { readApiMessages, saveApiMessages, saveTaskMessages } from "../core/task-persistence"
import { readRooMessages, saveRooMessages, saveTaskMessages } from "../core/task-persistence"

describe("History resume delegation - parent metadata transitions", () => {
beforeEach(() => {
Expand Down Expand Up @@ -83,7 +87,7 @@ describe("History resume delegation - parent metadata transitions", () => {

// Mock persistence reads to return empty arrays
vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

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

vi.mocked(readTaskMessages).mockResolvedValue(existingUiMessages as any)
vi.mocked(readApiMessages).mockResolvedValue(existingApiMessages as any)
vi.mocked(readRooMessages).mockResolvedValue(existingApiMessages as any)

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

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

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

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

vi.mocked(readTaskMessages).mockResolvedValue(existingUiMessages as any)
vi.mocked(readApiMessages).mockResolvedValue(existingApiMessages as any)
vi.mocked(readRooMessages).mockResolvedValue(existingApiMessages as any)

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

// Verify API history injection uses tool_result (not text fallback)
expect(saveApiMessages).toHaveBeenCalledWith(
// Verify API history injection uses tool-result (not text fallback)
expect(saveRooMessages).toHaveBeenCalledWith(
expect.objectContaining({
messages: expect.arrayContaining([
expect.objectContaining({
role: "user",
role: "tool",
content: expect.arrayContaining([
expect.objectContaining({
type: "tool_result",
tool_use_id: "toolu_abc123",
content: expect.stringContaining("Subtask c-tool completed"),
type: "tool-result",
toolCallId: "toolu_abc123",
output: expect.objectContaining({
value: expect.stringContaining("Subtask c-tool completed"),
}),
}),
]),
}),
Expand All @@ -277,15 +283,15 @@ describe("History resume delegation - parent metadata transitions", () => {
}),
)

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

// Verify the injected message is a user message with tool_result type
const injectedMsg = apiCall.messages[2]
expect(injectedMsg.role).toBe("user")
expect((injectedMsg.content[0] as any).type).toBe("tool_result")
expect((injectedMsg.content[0] as any).tool_use_id).toBe("toolu_abc123")
// Verify the injected message is a tool message with tool-result type
const injectedMsg = apiCall.messages[2] as any
expect(injectedMsg.role).toBe("tool")
expect((injectedMsg.content[0] as any).type).toBe("tool-result")
expect((injectedMsg.content[0] as any).toolCallId).toBe("toolu_abc123")
})

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

vi.mocked(readTaskMessages).mockResolvedValue(existingUiMessages as any)
vi.mocked(readApiMessages).mockResolvedValue(existingApiMessages as any)
vi.mocked(readRooMessages).mockResolvedValue(existingApiMessages as any)

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

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

vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

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

vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

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

vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

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

vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

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

vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

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

vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

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

// Mock read failures or empty returns
vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

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

expect(saveApiMessages).toHaveBeenCalledWith(
expect(saveRooMessages).toHaveBeenCalledWith(
expect.objectContaining({
messages: [
expect.objectContaining({
Expand Down
20 changes: 13 additions & 7 deletions src/__tests__/nested-delegation-resume.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,21 @@ vi.mock("vscode", () => {
vi.mock("../core/task-persistence/taskMessages", () => ({
readTaskMessages: vi.fn().mockResolvedValue([]),
}))
vi.mock("../core/task-persistence", () => ({
readApiMessages: vi.fn().mockResolvedValue([]),
saveApiMessages: vi.fn().mockResolvedValue(undefined),
saveTaskMessages: vi.fn().mockResolvedValue(undefined),
}))
vi.mock("../core/task-persistence", async (importOriginal) => {
const actual = await importOriginal<typeof import("../core/task-persistence")>()
return {
...actual,
readRooMessages: vi.fn().mockResolvedValue([]),
saveRooMessages: vi.fn().mockResolvedValue(undefined),
saveTaskMessages: vi.fn().mockResolvedValue(undefined),
}
})

import { attemptCompletionTool } from "../core/tools/AttemptCompletionTool"
import { ClineProvider } from "../core/webview/ClineProvider"
import type { Task } from "../core/task/Task"
import { readTaskMessages } from "../core/task-persistence/taskMessages"
import { readApiMessages, saveApiMessages, saveTaskMessages } from "../core/task-persistence"
import { readRooMessages, saveRooMessages, saveTaskMessages } from "../core/task-persistence"

describe("Nested delegation resume (A → B → C)", () => {
beforeEach(() => {
Expand Down Expand Up @@ -164,7 +168,7 @@ describe("Nested delegation resume (A → B → C)", () => {

// Empty histories for simplicity
vi.mocked(readTaskMessages).mockResolvedValue([])
vi.mocked(readApiMessages).mockResolvedValue([])
vi.mocked(readRooMessages).mockResolvedValue([])

// Step 1: C completes -> should reopen B automatically
const clineC = {
Expand All @@ -174,6 +178,7 @@ describe("Nested delegation resume (A → B → C)", () => {
historyItem: { parentTaskId: "B" },
providerRef: { deref: () => provider },
say: vi.fn().mockResolvedValue(undefined),
ask: vi.fn().mockResolvedValue({ response: "yesButtonClicked", text: undefined, images: undefined }),
emit: vi.fn(),
getTokenUsage: vi.fn(() => ({})),
toolUsage: {},
Expand Down Expand Up @@ -221,6 +226,7 @@ describe("Nested delegation resume (A → B → C)", () => {
historyItem: { parentTaskId: "A" },
providerRef: { deref: () => provider },
say: vi.fn().mockResolvedValue(undefined),
ask: vi.fn().mockResolvedValue({ response: "yesButtonClicked", text: undefined, images: undefined }),
emit: vi.fn(),
getTokenUsage: vi.fn(() => ({})),
toolUsage: {},
Expand Down
Loading
Loading