Skip to content

Commit d942014

Browse files
committed
test: add regression tests for PR #9542 tool_use partial blocks bug
These tests verify that presentAssistantMessage is called at stream end even when only tool_use blocks are partial. The tests explicitly show the difference between the fixed condition and the broken condition that was introduced in PR #9542.
1 parent 24bec99 commit d942014

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

src/core/task/__tests__/Task.spec.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,107 @@ describe("Cline", () => {
18451845
})
18461846
})
18471847
})
1848+
1849+
describe("Partial tool_use blocks at stream end", () => {
1850+
/**
1851+
* These tests verify that presentAssistantMessage is called at stream end
1852+
* even when only tool_use blocks are partial. This is critical because:
1853+
* - For XML protocol: tool_use blocks come from text parsing, not native events
1854+
* - If presentAssistantMessage isn't called, tools never execute and the task hangs
1855+
*
1856+
* A previous regression was caused by adding the condition:
1857+
* `partialBlocks.some((block) => block.type !== "tool_use")`
1858+
* which prevented presentAssistantMessage from being called when only
1859+
* tool_use blocks were partial.
1860+
*/
1861+
it("should call presentAssistantMessage when only tool_use blocks are partial", () => {
1862+
const cline = new Task({
1863+
provider: mockProvider,
1864+
apiConfiguration: mockApiConfig,
1865+
task: "test task",
1866+
startTask: false,
1867+
})
1868+
1869+
// Simulate the state at stream end with only a tool_use block that's partial
1870+
cline.assistantMessageContent = [
1871+
{
1872+
type: "tool_use" as const,
1873+
name: "read_file" as any,
1874+
params: { path: "test.txt" },
1875+
partial: true,
1876+
},
1877+
]
1878+
1879+
// Get partial blocks (simulating what happens at stream end)
1880+
const partialBlocks = cline.assistantMessageContent.filter((block) => block.partial)
1881+
1882+
// This is what the FIXED condition should evaluate to
1883+
const shouldPresentMessage = partialBlocks.length > 0
1884+
expect(shouldPresentMessage).toBe(true)
1885+
1886+
// This is what the BROKEN condition (PR #9542) would evaluate to
1887+
const brokenCondition = partialBlocks.length > 0 && partialBlocks.some((block) => block.type !== "tool_use")
1888+
expect(brokenCondition).toBe(false) // This was the bug - it evaluated to false!
1889+
})
1890+
1891+
it("should call presentAssistantMessage when text and tool_use blocks are partial", () => {
1892+
const cline = new Task({
1893+
provider: mockProvider,
1894+
apiConfiguration: mockApiConfig,
1895+
task: "test task",
1896+
startTask: false,
1897+
})
1898+
1899+
// Simulate the state at stream end with both text and tool_use blocks partial
1900+
cline.assistantMessageContent = [
1901+
{
1902+
type: "text" as const,
1903+
content: "Some text",
1904+
partial: true,
1905+
},
1906+
{
1907+
type: "tool_use" as const,
1908+
name: "read_file" as any,
1909+
params: { path: "test.txt" },
1910+
partial: true,
1911+
},
1912+
]
1913+
1914+
const partialBlocks = cline.assistantMessageContent.filter((block) => block.partial)
1915+
1916+
// Both conditions should be true in this case
1917+
const shouldPresentMessage = partialBlocks.length > 0
1918+
expect(shouldPresentMessage).toBe(true)
1919+
1920+
const brokenCondition = partialBlocks.length > 0 && partialBlocks.some((block) => block.type !== "tool_use")
1921+
expect(brokenCondition).toBe(true) // The old condition worked when text was also partial
1922+
})
1923+
1924+
it("should not call presentAssistantMessage when no blocks are partial", () => {
1925+
const cline = new Task({
1926+
provider: mockProvider,
1927+
apiConfiguration: mockApiConfig,
1928+
task: "test task",
1929+
startTask: false,
1930+
})
1931+
1932+
// Simulate completed blocks (no partial blocks)
1933+
cline.assistantMessageContent = [
1934+
{
1935+
type: "tool_use" as const,
1936+
name: "read_file" as any,
1937+
params: { path: "test.txt" },
1938+
partial: false,
1939+
},
1940+
]
1941+
1942+
const partialBlocks = cline.assistantMessageContent.filter((block) => block.partial)
1943+
1944+
// Neither condition should trigger presentAssistantMessage
1945+
const shouldPresentMessage = partialBlocks.length > 0
1946+
expect(shouldPresentMessage).toBe(false)
1947+
})
1948+
})
18481949
})
18491950

18501951
describe("Queued message processing after condense", () => {

0 commit comments

Comments
 (0)